ホーム » IMDB

IMDB」カテゴリーアーカイブ

TensorFlow 2.0 : Tutorials : Keras ML 基本 :- 前処理されたテキストでテキスト分類 : 映画レビュー

TensorFlow 2.0 : Beginner Tutorials : Keras ML 基本 :- 前処理されたテキストでテキスト分類 : 映画レビュー (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/03/2019

* 本ページは、TensorFlow org サイトの TF 2.0 – Beginner Tutorials – ML basics with Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

Keras ML 基本 :- 前処理されたテキストでテキスト分類 : 映画レビュー

このノートブックは (映画) レビューのテキストを使用して映画レビューを肯定的か否定的として分類します。これは二値 — あるいは 2 クラス — 分類の例で、重要で広く応用可能な種類の機械学習問題です。

私達は IMDB データセット を使用します、これは Internet Movie Database からの 50,000 映画レビューのテキストを含みます。これらは訓練のための 25,000 レビューとテストのための 25,000 レビューに分割されます。訓練とテストセットは均等です、それらがポジティブとネガティブ・レビューの同じ数を含むことを意味します。

この notebook は tf.keras を使用します、TensorFlow でモデルを構築して訓練するための高位 API です。tf.keras を使用したより進んだテキスト分類チュートリアルについては、MLCC テキスト分類ガイド を見てください。

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
from tensorflow import keras

import tensorflow_datasets as tfds
tfds.disable_progress_bar()

import numpy as np

print(tf.__version__)
2.0.0

 

IMDB データセットをダウンロードする

IMDB 映画レビュー・データセットは tfds でパッケージ化されています。

それは既にレビュー (単語のシークエンス) が整数のシークエンスに変換されるように前処理されていて、そこでは各整数は辞書の特定の単語を表しています。

次のコードは IMDB データセットを貴方のマシンにダウンロードします (あるいは既にそれをダウンロードしているのであればキャッシュされたコピーを使用します) :

貴方自身のテキストをエンコードするには Loading text チュートリアル を見てください

(train_data, test_data), info = tfds.load(
    # Use the version pre-encoded with an ~8k vocabulary.
    'imdb_reviews/subwords8k', 
    # Return the train/test datasets as a tuple.
    split = (tfds.Split.TRAIN, tfds.Split.TEST),
    # Return (example, label) pairs from the dataset (instead of a dictionary).
    as_supervised=True,
    # Also return the `info` structure. 
    with_info=True)
Downloading and preparing dataset imdb_reviews (80.23 MiB) to /home/kbuilder/tensorflow_datasets/imdb_reviews/subwords8k/0.1.0...
WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

Dataset imdb_reviews downloaded and prepared to /home/kbuilder/tensorflow_datasets/imdb_reviews/subwords8k/0.1.0. Subsequent calls will reuse this data.

 

エンコーダを試す

データセットの info はテキスト・エンコーダ ( tfds.features.text.SubwordTextEncoder ) を含みます。

encoder = info.features['text'].encoder
print ('Vocabulary size: {}'.format(encoder.vocab_size))
Vocabulary size: 8185

このテキスト・エンコーダは任意の文字列を可逆的にエンコードします :

sample_string = 'Hello TensorFlow.'

encoded_string = encoder.encode(sample_string)
print ('Encoded string is {}'.format(encoded_string))

original_string = encoder.decode(encoded_string)
print ('The original string: "{}"'.format(original_string))

assert original_string == sample_string
Encoded string is [4025, 222, 6307, 2327, 4043, 2120, 7975]
The original string: "Hello TensorFlow."

エンコーダは文字列を単語が辞書にない場合にはそれを部分単語か文字に分解することによりエンコードします。そのためより多くの文字列がデータセットに類似していれば、エンコードされた表現はより短くなります。

for ts in encoded_string:
  print ('{} ----> {}'.format(ts, encoder.decode([ts])))
4025 ----> Hell
222 ----> o 
6307 ----> Ten
2327 ----> sor
4043 ----> Fl
2120 ----> ow
7975 ----> .

 

データを調査する

データのフォーマットを理解するために少し時間をつかいましょう。データセットは前処理されています : 各サンプルは映画レビューの単語を表わす整数の配列です。

レビューのテキストは整数に変換されます、そこでは各整数は辞書の特定の単語ピースを表します。

各ラベルは 0 か 1 の整数値で、そこでは 0 は否定的なレビューで、1 は肯定的なレビューです。

ここに最初のレビューがどのように見えるがあります :

for train_example, train_label in train_data.take(1):
  print('Encoded text:', train_example[:10].numpy())
  print('Label:', train_label.numpy())
Encoded text: [ 249    4  277  309  560    6 6639 4574    2   12]
Label: 1

info 構造はエンコーダ/デコーダを含みます。エンコーダは元のテキストをリカバーするために使用できます :

encoder.decode(train_example)
"As a lifelong fan of Dickens, I have invariably been disappointed by adaptations of his novels.

Although his works presented an extremely accurate re-telling of human life at every level in Victorian Britain, throughout them all was a pervasive thread of humour that could be both playful or sarcastic as the narrative dictated. In a way, he was a literary caricaturist and cartoonist. He could be serious and hilarious in the same sentence. He pricked pride, lampooned arrogance, celebrated modesty, and empathised with loneliness and poverty. It may be a cliché, but he was a people's writer.

And it is the comedy that is so often missing from his interpretations. At the time of writing, Oliver Twist is being dramatised in serial form on BBC television. All of the misery and cruelty is their, but non of the humour, irony, and savage lampoonery. The result is just a dark, dismal experience: the story penned by a journalist rather than a novelist. It's not really Dickens at all.

'Oliver!', on the other hand, is much closer to the mark. The mockery of officialdom is perfectly interpreted, from the blustering beadle to the drunken magistrate. The classic stand-off between the beadle and Mr Brownlow, in which the law is described as 'a ass, a idiot' couldn't have been better done. Harry Secombe is an ideal choice.

But the blinding cruelty is also there, the callous indifference of the state, the cold, hunger, poverty and loneliness are all presented just as surely as The Master would have wished.

And then there is crime. Ron Moody is a treasure as the sleazy Jewish fence, whilst Oliver Reid has Bill Sykes to perfection.

Perhaps not surprisingly, Lionel Bart - himself a Jew from London's east-end - takes a liberty with Fagin by re-interpreting him as a much more benign fellow than was Dicken's original. In the novel, he was utterly ruthless, sending some of his own boys to the gallows in order to protect himself (though he was also caught and hanged). Whereas in the movie, he is presented as something of a wayward father-figure, a sort of charitable thief rather than a corrupter of children, the latter being a long-standing anti-semitic sentiment. Otherwise, very few liberties are taken with Dickens's original. All of the most memorable elements are included. Just enough menace and violence is retained to ensure narrative fidelity whilst at the same time allowing for children' sensibilities. Nancy is still beaten to death, Bullseye narrowly escapes drowning, and Bill Sykes gets a faithfully graphic come-uppance.

Every song is excellent, though they do incline towards schmaltz. Mark Lester mimes his wonderfully. Both his and my favourite scene is the one in which the world comes alive to 'who will buy'. It's schmaltzy, but it's Dickens through and through.

I could go on. I could commend the wonderful set-pieces, the contrast of the rich and poor. There is top-quality acting from more British regulars than you could shake a stick at.

I ought to give it 10 points, but I'm feeling more like Scrooge today. Soak it up with your Christmas dinner. No original has been better realised."

 

訓練のためにデータを準備する

モデルのための訓練データのバッチを作成することを望みます。レビューは総て異なる長さですので、バッチ処理する間にシークエンスをゼロパッドするために padded_batch を使用します :

BUFFER_SIZE = 1000

train_batches = (
    train_data
    .shuffle(BUFFER_SIZE)
    .padded_batch(32, train_data.output_shapes))

test_batches = (
    test_data
    .padded_batch(32, train_data.output_shapes))

各バッチは (batch_size, sequence_length) の shape を持ちます、パディングは動的ですので各バッチは異なる長さを持つでしょう :

for example_batch, label_batch in train_batches.take(2):
  print("Batch shape:", example_batch.shape)
  print("label shape:", label_batch.shape)
Batch shape: (32, 1186)
label shape: (32,)
Batch shape: (32, 1111)
label shape: (32,)

 

モデルを構築する

ニューラルネットワークは層をスタックすることにより作成されます — これは 2 つの主要なアーキテクチャ的な決定を必要とします :

  • モデルで幾つの層を使用するか?
  • 各層のために幾つの隠れユニットを使用するか?

このサンプルでは、入力データは単語インデックスの配列から成ります。予測するラベルは 0 か 1 です。この問題のために “Continuous bag of words (CBOW)” スタイルのモデルを構築しましょう :

警告: このモデルはマスキングを使用しませんので、入力の一部としてゼロ・パディングが使用されますので、パディング長は出力に影響するかもしれません。これを修正するには、マスキングとパディング・ガイド を見てください。

model = keras.Sequential([
  keras.layers.Embedding(encoder.vocab_size, 16),
  keras.layers.GlobalAveragePooling1D(),
  keras.layers.Dense(1, activation='sigmoid')])

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 16)          130960    
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 1)                 17        
=================================================================
Total params: 130,977
Trainable params: 130,977
Non-trainable params: 0

分類器を構築するために層はシーケンシャルにスタックされます :

  1. 最初の層は Embedding 層です。この層は整数エンコードされた語彙を取って各単語インデックスのための埋め込みベクトルを検索します。これらのベクトルはモデルが訓練されるときに学習されます。ベクトルは出力配列に次元を追加します。結果としての次元は (batch, sequence, embedding) です。
  2. 次に、GlobalAveragePooling1D 層は各サンプルについて sequence 次元に渡り平均することにより固定長出力ベクトルを返します。これは可能な最も単純な方法でモデルが可変長の入力を扱うことを可能にします。
  3. この固定長出力ベクトルは 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  4. 最後の層は単一の出力ノードに密に接続されています。sigmoid 活性化関数を使用して、この値は 0 と 1 の間の浮動小数点で、確率、または確信レベルを表します。

 

隠れユニット

上のモデルは入力と出力の間に、2 つの中間層あるいは「隠れ」層を持ちます。出力 (ユニット、ノード、またはニューロン) の数は層のための具象空間の次元です。換言すれば、内部表現を学習するときにネットワークが許容される自由度の総量です。

モデルがより多くの隠れユニット (より高い次元の表現空間) and/or より多くの層を持てば、ネットワークはより複雑な表現を学習できます。けれども、それはネットワークをより計算的に高価にして望まないパターンを学習することに繋がるかもしれません — このパターンは訓練データ上の性能を改善しますがテストデータ上ではそうではないものです。これは overfitting と呼ばれ、後でそれを調査します。

 

損失関数と optimizer

モデルは訓練のために損失関数と optimizer を必要とします。これは二値分類問題でモデルは確率を出力します (sigmoid 活性を持つシングルユニット層) ので、binary_crossentropy 損失関数を使用します。

これは損失関数のための唯一の選択ではありません、例えば mean_squared_error を選択できるでしょう。しかし一般的に binary_crossentropy は確率を扱うためにはより良いです — それは確率分布間、あるいは私達のケースでは、正解の分布と予測の間の「距離」を測ります。

後で、回帰問題 (例えば、家の価格を予測する) を調べているときに、mean squared error と呼ばれるもう一つの損失関数をどのように使用するかを見ます。

さて、optimizer と損失関数を使用するためにモデルを configure します :

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

 

モデルを訓練する

Dataset オブジェクトを model の fit 関数に渡すことによりモデルを訓練します。エポック数を設します。

history = model.fit(train_batches,
                    epochs=10,
                    validation_data=test_batches,
                    validation_steps=30)
Epoch 1/10
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/nn_impl.py:183: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/nn_impl.py:183: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

782/782 [==============================] - 8s 10ms/step - loss: 0.6822 - accuracy: 0.5978 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
782/782 [==============================] - 5s 7ms/step - loss: 0.6213 - accuracy: 0.7495 - val_loss: 0.5959 - val_accuracy: 0.7635
Epoch 3/10
782/782 [==============================] - 5s 7ms/step - loss: 0.5426 - accuracy: 0.8070 - val_loss: 0.5296 - val_accuracy: 0.8073
Epoch 4/10
782/782 [==============================] - 5s 7ms/step - loss: 0.4762 - accuracy: 0.8382 - val_loss: 0.4752 - val_accuracy: 0.8313
Epoch 5/10
782/782 [==============================] - 5s 7ms/step - loss: 0.4220 - accuracy: 0.8644 - val_loss: 0.4334 - val_accuracy: 0.8500
Epoch 6/10
782/782 [==============================] - 5s 7ms/step - loss: 0.3823 - accuracy: 0.8769 - val_loss: 0.4054 - val_accuracy: 0.8562
Epoch 7/10
782/782 [==============================] - 5s 7ms/step - loss: 0.3508 - accuracy: 0.8861 - val_loss: 0.3760 - val_accuracy: 0.8677
Epoch 8/10
782/782 [==============================] - 5s 7ms/step - loss: 0.3234 - accuracy: 0.8931 - val_loss: 0.3566 - val_accuracy: 0.8750
Epoch 9/10
782/782 [==============================] - 5s 7ms/step - loss: 0.3033 - accuracy: 0.9000 - val_loss: 0.3406 - val_accuracy: 0.8802
Epoch 10/10
782/782 [==============================] - 5s 7ms/step - loss: 0.2872 - accuracy: 0.9036 - val_loss: 0.3285 - val_accuracy: 0.8844

 

モデルを評価する

そしてモデルどのように遂行するか見ましょう。2 つの値が返されます。損失 (エラーを表わす数字です、より低ければより良いです)、そして精度です。

loss, accuracy = model.evaluate(test_batches)

print("Loss: ", loss)
print("Accuracy: ", accuracy)
782/782 [==============================] - 3s 4ms/step - loss: 0.3320 - accuracy: 0.8772
Loss:  0.33202573521743955
Accuracy:  0.8772

このかなり素朴なアプローチは約 87 % の精度を得ます。より進んだアプローチでは、モデルは 95 % に近づくはずです。

 

時間に渡る精度と損失のグラフを作成する

model.fit() は History オブジェクトを返します、これは訓練の間に発生した総てを持つ辞書を含みます :

history_dict = history.history
history_dict.keys()
dict_keys(['loss', 'val_accuracy', 'accuracy', 'val_loss'])

4 つのエントリがあります: 訓練と検証の間に各々監視されたメトリックのために一つ (ずつ) です。比較のために訓練と検証精度に加えて、訓練と検証損失をプロットするためにこれらを使用することができます :

import matplotlib.pyplot as plt

acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
<Figure size 640x480 with 1 Axes>
plt.clf()   # clear figure

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')

plt.show()

このプロットでは、点線は訓練損失と精度を表し、そして実線は検証損失と精度です。

訓練損失は各エポックとともに減少して訓練精度は各エポックとともに増加することに気がつくでしょう。これは勾配降下最適化を使用するときに期待されるものです — それは総ての反復で望まれる量を最小化するはずです。

これは検証損失と精度については当てはまりません — それらは約 20 エポック後に最大になるようです。これは overfitting の例です : モデルは、それが前に決して見ていないデータ上で行なうよりも訓練データ上でより上手く遂行します。このポイント後、モデルは過剰に最適化されてテストデータに一般化されない訓練データに固有の表現を学習します。

この特定のケースのためには、単純に 20 程度のエポック後に訓練を停止することで overfitting を回避できるでしょう。後で、これを callback で自動的にどのように行なうかを見るでしょう。

 

以上



TensorFlow 2.0 : Tutorials : Keras ML 基本 :- TensorFlow Hub でテキスト分類 : 映画レビュー

TensorFlow 2.0 : Beginner Tutorials : Keras ML 基本 :- TensorFlow Hub でテキスト分類 : 映画レビュー (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/02/2019

* 本ページは、TensorFlow org サイトの TF 2.0 – Beginner Tutorials – ML basics with Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

Tutorials : Keras ML 基本 :- TensorFlow Hub でテキスト分類 : 映画レビュー

このノートブックは (映画) レビューのテキストを使用して映画レビューをポジティブ (肯定的) かネガティブ (否定的) として分類します。これは二値 — あるいは 2 クラス — 分類の例で、重要で広く利用可能な種類の機械学習問題です。

このチュートリアルは TensorFlow Hub と Keras による転移学習の基本的な応用を実演します。

私達は IMDB データセット を使用します、これは Internet Movie Database からの 50,000 映画レビューのテキストを含みます。これらは訓練のための 25,000 レビューとテストのための 25,000 レビューに分割されます。訓練とテストセットは均等です、つまりそれらがポジティブとネガティブ・レビューの同じ数を含むことを意味します。

この notebook は tf.keras、TensorFlow でモデルを構築して訓練するための高位 API、そして TensorFlow Hub、転移学習のためのライブラリとプラットフォームを使用します。tf.keras を使用したより進んだテキスト分類チュートリアルについては、MLCC テキスト分類ガイド を見てください。

from __future__ import absolute_import, division, print_function, unicode_literals

import numpy as np

import tensorflow as tf

import tensorflow_hub as hub
import tensorflow_datasets as tfds

print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub version: ", hub.__version__)
print("GPU is", "available" if tf.config.experimental.list_physical_devices("GPU") else "NOT AVAILABLE")
Version:  2.0.0-rc2
Eager mode:  True
Hub version:  0.6.0
GPU is available

 

IMDB データセットをダウンロードする

IMDB データセットは imdb reviews (訳注: リンク切れ、こちら を参照) 上かまたは TensorFlow データセット 上で利用可能です。次のコードは IMDB データセットを貴方のマシン (または colab ランタイム) にダウンロードします :

# Split the training set into 60% and 40%, so we'll end up with 15,000 examples
# for training, 10,000 examples for validation and 25,000 examples for testing.
train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])

(train_data, validation_data), test_data = tfds.load(
    name="imdb_reviews", 
    split=(train_validation_split, tfds.Split.TEST),
    as_supervised=True)
Downloading and preparing dataset imdb_reviews (80.23 MiB) to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/0.1.0...

HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Completed...', max=1, style=ProgressStyl…
HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Size...', max=1, style=ProgressStyle(des…





HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=10, style=ProgressStyle(description_width=…
WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…


HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=10, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…


HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=20, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
Dataset imdb_reviews downloaded and prepared to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/0.1.0. Subsequent calls will reuse this data.

 

データを調査する

データのフォーマットを理解するために少し時間を使いましょう。各サンプルは映画レビューを表わすセンテンスと対応するラベルです。センテンスはどのような方法でも前処理されていません。各ラベルは 0 か 1 の整数値で、そこでは 0 は否定的なレビューで、1 は肯定的なレビューです。

最初の 10 サンプルをプリントしましょう。

train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
train_examples_batch
<tf.Tensor: id=219, shape=(10,), dtype=string, numpy=
array([b"As a lifelong fan of Dickens, I have invariably been disappointed by adaptations of his novels.

Although his works presented an extremely accurate re-telling of human life at every level in Victorian Britain, throughout them all was a pervasive thread of humour that could be both playful or sarcastic as the narrative dictated. In a way, he was a literary caricaturist and cartoonist. He could be serious and hilarious in the same sentence. He pricked pride, lampooned arrogance, celebrated modesty, and empathised with loneliness and poverty. It may be a clich\xc3\xa9, but he was a people's writer.

And it is the comedy that is so often missing from his interpretations. At the time of writing, Oliver Twist is being dramatised in serial form on BBC television. All of the misery and cruelty is their, but non of the humour, irony, and savage lampoonery. The result is just a dark, dismal experience: the story penned by a journalist rather than a novelist. It's not really Dickens at all.

'Oliver!', on the other hand, is much closer to the mark. The mockery of officialdom is perfectly interpreted, from the blustering beadle to the drunken magistrate. The classic stand-off between the beadle and Mr Brownlow, in which the law is described as 'a ass, a idiot' couldn't have been better done. Harry Secombe is an ideal choice.

But the blinding cruelty is also there, the callous indifference of the state, the cold, hunger, poverty and loneliness are all presented just as surely as The Master would have wished.

And then there is crime. Ron Moody is a treasure as the sleazy Jewish fence, whilst Oliver Reid has Bill Sykes to perfection.

Perhaps not surprisingly, Lionel Bart - himself a Jew from London's east-end - takes a liberty with Fagin by re-interpreting him as a much more benign fellow than was Dicken's original. In the novel, he was utterly ruthless, sending some of his own boys to the gallows in order to protect himself (though he was also caught and hanged). Whereas in the movie, he is presented as something of a wayward father-figure, a sort of charitable thief rather than a corrupter of children, the latter being a long-standing anti-semitic sentiment. Otherwise, very few liberties are taken with Dickens's original. All of the most memorable elements are included. Just enough menace and violence is retained to ensure narrative fidelity whilst at the same time allowing for children' sensibilities. Nancy is still beaten to death, Bullseye narrowly escapes drowning, and Bill Sykes gets a faithfully graphic come-uppance.

Every song is excellent, though they do incline towards schmaltz. Mark Lester mimes his wonderfully. Both his and my favourite scene is the one in which the world comes alive to 'who will buy'. It's schmaltzy, but it's Dickens through and through.

I could go on. I could commend the wonderful set-pieces, the contrast of the rich and poor. There is top-quality acting from more British regulars than you could shake a stick at.

I ought to give it 10 points, but I'm feeling more like Scrooge today. Soak it up with your Christmas dinner. No original has been better realised.", b"Oh yeah! Jenna Jameson did it again! Yeah Baby! This movie rocks. It was one of the 1st movies i saw of her. And i have to say i feel in love with her, she was great in this move.

Her performance was outstanding and what i liked the most was the scenery and the wardrobe it was amazing you can tell that they put a lot into the movie the girls cloth were amazing.

I hope this comment helps and u can buy the movie, the storyline is awesome is very unique and i'm sure u are going to like it. Jenna amazed us once more and no wonder the movie won so many awards. Her make-up and wardrobe is very very sexy and the girls on girls scene is amazing. specially the one where she looks like an angel. It's a must see and i hope u share my interests", b"I saw this film on True Movies (which automatically made me sceptical) but actually - it was good. Why? Not because of the amazing plot twists or breathtaking dialogue (of which there is little) but because actually, despite what people say I thought the film was accurate in it's depiction of teenagers dealing with pregnancy.

It's NOT Dawson's Creek, they're not graceful, cool witty characters who breeze through sexuality with effortless knowledge. They're kids and they act like kids would.

They're blunt, awkward and annoyingly confused about everything. Yes, this could be by accident and they could just be bad actors but I don't think so. Dermot Mulroney gives (when not trying to be cool) a very believable performance and I loved him for it. Patricia Arquette IS whiny and annoying, but she was pregnant and a teenagers? The combination of the two isn't exactly lavender on your pillow. The plot was VERY predictable and but so what? I believed them, his stress and inability to cope - her brave, yet slightly misguided attempts to bring them closer together. I think the characters, acted by anyone else, WOULD indeed have been annoying and unbelievable but they weren't. It reflects the surreality of the situation they're in, that he's sitting in class and she walks on campus with the baby. I felt angry at her for that, I felt angry at him for being such a child and for blaming her. I felt it all.

In the end, I loved it and would recommend it.

Watch out for the scene where Dermot Mulroney runs from the disastrous counselling session - career performance.", b'This was a wonderfully clever and entertaining movie that I shall never tire of watching many, many times. The casting was magnificent in matching up the young with the older characters. There are those of us out here who really do appreciate good actors and an intelligent story format. As for Judi Dench, she is beautiful and a gift to any kind of production in which she stars. I always make a point to see Judi Dench in all her performances. She is a superb actress and a pleasure to watch as each transformation of her character comes to life. I can only be grateful when I see such an outstanding picture for most of the motion pictures made more recently lack good characters, good scripts and good acting. The movie public needs heroes, not deviant manikins, who lack ingenuity and talent. How wonderful to see old favorites like Leslie Caron, Olympia Dukakis and Cleo Laine. I would like to see this movie win the awards it deserves. Thank you again for a tremendous night of entertainment. I congratulate the writer, director, producer, and all those who did such a fine job.', b'I have no idea what the other reviewer is talking about- this was a wonderful movie, and created a sense of the era that feels like time travel. The characters are truly young, Mary is a strong match for Byron, Claire is juvenile and a tad annoying, Polidori is a convincing beaten-down sycophant... all are beautiful, curious, and decadent... not the frightening wrecks they are in Gothic.

Gothic works as an independent piece of shock film, and I loved it for different reasons, but this works like a Merchant and Ivory film, and was from my readings the best capture of what the summer must have felt like. Romantic, yes, but completely rekindles my interest in the lives of Shelley and Byron every time I think about the film. One of my all-time favorites.', b"This was soul-provoking! I am an Iranian, and living in th 21st century, I didn't know that such big tribes have been living in such conditions at the time of my grandfather!

You see that today, or even in 1925, on one side of the world a lady or a baby could have everything served for him or her clean and on-demand, but here 80 years ago, people ventured their life to go to somewhere with more grass. It's really interesting that these Persians bear those difficulties to find pasture for their sheep, but they lose many the sheep on their way.

I praise the Americans who accompanied this tribe, they were as tough as Bakhtiari people.", b'Just because someone is under the age of 10 does not mean they are stupid. If your child likes this film you\'d better have him/her tested. I am continually amazed at how so many people can be involved in something that turns out so bad. This "film" is a showcase for digital wizardry AND NOTHING ELSE. The writing is horrid. I can\'t remember when I\'ve heard such bad dialogue. The songs are beyond wretched. The acting is sub-par but then the actors were not given much. Who decided to employ Joey Fatone? He cannot sing and he is ugly as sin.

The worst thing is the obviousness of it all. It is as if the writers went out of their way to make it all as stupid as possible. Great children\'s movies are wicked, smart and full of wit - films like Shrek and Toy Story in recent years, Willie Wonka and The Witches to mention two of the past. But in the continual dumbing-down of American more are flocking to dreck like Finding Nemo (yes, that\'s right), the recent Charlie & The Chocolate Factory and eye-crossing trash like Red Riding Hood.', b"I absolutely LOVED this movie when I was a kid. I cried every time I watched it. It wasn't weird to me. I totally identified with the characters. I would love to see it again (and hope I wont be disappointed!). Pufnstuf rocks!!!! I was really drawn in to the fantasy world. And to me the movie was loooong. I wonder if I ever saw the series and have confused them? The acting I thought was strong. I loved Jack Wilde. He was so dreamy to an 10 year old (when I first saw the movie, not in 1970. I can still remember the characters vividly. The flute was totally believable and I can still 'feel' the evil woods. Witchy poo was scary - I wouldn't want to cross her path.", b'A very close and sharp discription of the bubbling and dynamic emotional world of specialy one 18year old guy, that makes his first experiences in his gay love to an other boy, during an vacation with a part of his family.

I liked this film because of his extremly clear and surrogated storytelling , with all this "Sound-close-ups" and quiet moments wich had been full of intensive moods.

', b"This is the most depressing film I have ever seen. I first saw it as a child and even thinking about it now really upsets me. I know it was set in a time when life was hard and I know these people were poor and the crops were vital. Yes, I get all that. What I find hard to take is I can't remember one single light moment in the entire film. Maybe it was true to life, I don't know. I'm quite sure the acting was top notch and the direction and quality of filming etc etc was wonderful and I know that every film can't have a happy ending but as a family film it is dire in my opinion.

I wouldn't recommend it to anyone who wants to be entertained by a film. I can't stress enough how this film affected me as a child. I was talking about it recently and all the sad memories came flooding back. I think it would have all but the heartless reaching for the Prozac."], dtype=object)>

 
最初の 10 ラベルもプリントしましょう。

train_labels_batch
<tf.Tensor: id=220, shape=(10,), dtype=int64, numpy=array([1, 1, 1, 1, 1, 1, 0, 1, 1, 0])>

 

モデルを構築する

ニューラルネットワークは層をスタックすることにより作成されます — これは 3 つの主要なアーキテクチャ的な決定を必要とします :

  • テキストをどのように表現するか?
  • モデルで幾つの層を使用するか?
  • 各層のために幾つの隠れユニットを使用するか?

このサンプルでは、入力データはセンテンスから成ります。予測するラベルは 0 か 1 です。

テキストを表わす一つの方法はセンテンスを埋め込みベクトルに変換することです。最初の層として事前訓練されたテキスト埋め込みを使用できます、これは 3 つの優位点を持ちます : * テキスト前処理について心配しなくてかまいません、* 転移学習からの恩恵を受けることができます、* 埋め込みは固定サイズを持ちますので、処理がより容易です。

このサンプルのために TensorFlow Hub から google/tf2-preview/gnews-swivel-20dim/1 と呼称される事前訓練されたテキスト埋め込みモデルを使用します。

このチュートリアルの目的でテストするために他の 3 つの事前訓練されたモデルがあります :

最初にセンテンスを埋め込むために TensorFlow Hub モデルを使用する Keras 層を作成しましょう、そして 2, 3 の入力サンプル上でそれを試しましょう。入力テキストの長さがどうであれ、埋め込みの出力 shape は : (num_examples, embedding_dimension) であることに注意してください。

embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1"
hub_layer = hub.KerasLayer(embedding, input_shape=[], 
                           dtype=tf.string, trainable=True)
hub_layer(train_examples_batch[:3])
<tf.Tensor: id=402, shape=(3, 20), dtype=float32, numpy=
array([[ 3.9819887 , -4.4838037 ,  5.177359  , -2.3643482 , -3.2938678 ,
        -3.5364532 , -2.4786978 ,  2.5525482 ,  6.688532  , -2.3076782 ,
        -1.9807833 ,  1.1315885 , -3.0339816 , -0.7604128 , -5.743445  ,
         3.4242578 ,  4.790099  , -4.03061   , -5.992149  , -1.7297493 ],
       [ 3.4232912 , -4.230874  ,  4.1488533 , -0.29553518, -6.802391  ,
        -2.5163853 , -4.4002395 ,  1.905792  ,  4.7512794 , -0.40538004,
        -4.3401685 ,  1.0361497 ,  0.9744097 ,  0.71507156, -6.2657013 ,
         0.16533905,  4.560262  , -1.3106939 , -3.1121316 , -2.1338716 ],
       [ 3.8508697 , -5.003031  ,  4.8700504 , -0.04324996, -5.893603  ,
        -5.2983093 , -4.004676  ,  4.1236343 ,  6.267754  ,  0.11632943,
        -3.5934832 ,  0.8023905 ,  0.56146765,  0.9192484 , -7.3066816 ,
         2.8202746 ,  6.2000837 , -3.5709393 , -4.564525  , -2.305622  ]],
      dtype=float32)>

さて完全なモデルを構築しましょう :

model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
keras_layer (KerasLayer)     (None, 20)                400020    
_________________________________________________________________
dense (Dense)                (None, 16)                336       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 400,373
Trainable params: 400,373
Non-trainable params: 0
_________________________________________________________________

分類器を構築するために層はシーケンシャルにスタックされます :

  1. 最初の層は TensorFlow Hub 層です。この層はセンテンスをその埋め込みベクトルにマップするために事前訓練された Saved Model を使用します。私達が使用している事前訓練されたテキスト埋め込みモデル (google/tf2-preview/gnews-swivel-20dim/1) はセンテンスをトークンに分割し、各トークンを埋め込んでから埋め込みを結合します。結果の次元は : (num_examples, embedding_dimension) です。
  2. この固定長出力ベクトルは 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  3. 最後の層は単一の出力ノードに密に接続されています。sigmoid 活性化関数を使用します、この値は 0 と 1 の間の浮動小数点で、確率、または確信レベルを表します。

モデルを compile しましょう。

 

損失関数と optimizer

モデルは訓練のために損失関数と optimizer を必要とします。これは二値分類問題でモデルは確率を出力します (sigmoid 活性を持つシングルユニット層) ので、binary_crossentropy 損失関数を使用します。

これは損失関数のための唯一の選択ではありません、例えば、mean_squared_error を選択できるでしょう。しかし、一般に、binary_crossentropy は確率を扱うためにはより良いです — それは確率分布間、あるいは私達のケースでは、正解の分布と予測の間の「距離」を測ります。

後で、回帰問題 (例えば、家の価格を予測する) を調べているときに、mean squared error と呼ばれるもう一つの損失関数をどのように使用するかを見ます。

今は、optimizer と損失関数を使用するためにモデルを configure します :

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

 

モデルを訓練する

モデルを 512 サンプルのミニバッチで 20 エポック訓練します。これは x_train と y_train tensor の総てのサンプルに渡る 20 反復です。訓練の間、検証セットからの 10,000 サンプル上でモデルの損失と精度を監視します :

history = model.fit(train_data.shuffle(10000).batch(512),
                    epochs=20,
                    validation_data=validation_data.batch(512),
                    verbose=1)
Epoch 1/20
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/nn_impl.py:183: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/nn_impl.py:183: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

30/30 [==============================] - 5s 169ms/step - loss: 0.7772 - accuracy: 0.4825 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/20
30/30 [==============================] - 4s 126ms/step - loss: 0.6790 - accuracy: 0.5987 - val_loss: 0.6471 - val_accuracy: 0.6277
Epoch 3/20
30/30 [==============================] - 4s 131ms/step - loss: 0.6215 - accuracy: 0.6671 - val_loss: 0.6004 - val_accuracy: 0.6807
Epoch 4/20
30/30 [==============================] - 4s 125ms/step - loss: 0.5772 - accuracy: 0.7111 - val_loss: 0.5644 - val_accuracy: 0.7126
Epoch 5/20
30/30 [==============================] - 4s 127ms/step - loss: 0.5350 - accuracy: 0.7450 - val_loss: 0.5296 - val_accuracy: 0.7412
Epoch 6/20
30/30 [==============================] - 4s 125ms/step - loss: 0.4988 - accuracy: 0.7695 - val_loss: 0.4971 - val_accuracy: 0.7648
Epoch 7/20
30/30 [==============================] - 4s 128ms/step - loss: 0.4615 - accuracy: 0.7970 - val_loss: 0.4674 - val_accuracy: 0.7849
Epoch 8/20
30/30 [==============================] - 4s 125ms/step - loss: 0.4270 - accuracy: 0.8168 - val_loss: 0.4373 - val_accuracy: 0.8011
Epoch 9/20
30/30 [==============================] - 4s 127ms/step - loss: 0.3882 - accuracy: 0.8333 - val_loss: 0.4144 - val_accuracy: 0.8120
Epoch 10/20
30/30 [==============================] - 4s 123ms/step - loss: 0.3618 - accuracy: 0.8483 - val_loss: 0.3904 - val_accuracy: 0.8264
Epoch 11/20
30/30 [==============================] - 4s 125ms/step - loss: 0.3338 - accuracy: 0.8647 - val_loss: 0.3737 - val_accuracy: 0.8309
Epoch 12/20
30/30 [==============================] - 4s 124ms/step - loss: 0.3116 - accuracy: 0.8763 - val_loss: 0.3590 - val_accuracy: 0.8385
Epoch 13/20
30/30 [==============================] - 4s 127ms/step - loss: 0.2864 - accuracy: 0.8881 - val_loss: 0.3430 - val_accuracy: 0.8499
Epoch 14/20
30/30 [==============================] - 4s 125ms/step - loss: 0.2647 - accuracy: 0.8993 - val_loss: 0.3326 - val_accuracy: 0.8552
Epoch 15/20
30/30 [==============================] - 4s 127ms/step - loss: 0.2458 - accuracy: 0.9080 - val_loss: 0.3240 - val_accuracy: 0.8599
Epoch 16/20
30/30 [==============================] - 4s 126ms/step - loss: 0.2321 - accuracy: 0.9145 - val_loss: 0.3165 - val_accuracy: 0.8654
Epoch 17/20
30/30 [==============================] - 4s 128ms/step - loss: 0.2173 - accuracy: 0.9216 - val_loss: 0.3111 - val_accuracy: 0.8687
Epoch 18/20
30/30 [==============================] - 4s 123ms/step - loss: 0.2023 - accuracy: 0.9286 - val_loss: 0.3070 - val_accuracy: 0.8697
Epoch 19/20
30/30 [==============================] - 4s 126ms/step - loss: 0.1918 - accuracy: 0.9340 - val_loss: 0.3047 - val_accuracy: 0.8713
Epoch 20/20
30/30 [==============================] - 4s 124ms/step - loss: 0.1793 - accuracy: 0.9388 - val_loss: 0.3087 - val_accuracy: 0.8717

 

モデルを評価する

そしてモデルどのように遂行するか見ましょう。2 つの値が返されます。損失 (エラーを表わす数字です、より低ければより良いです)、そして精度です。

results = model.evaluate(test_data.batch(512), verbose=0)
for name, value in zip(model.metrics_names, results):
  print("%s: %.3f" % (name, value))
loss: 0.327
accuracy: 0.861

このかなり素朴なアプローチは約 87 % の精度を得ます。より進んだアプローチでは、モデルは 95 % に近づくはずです。

 

Further reading

文字列入力で作業するためのより一般的な方法のために、そして訓練の間の精度と損失の進捗のより詳細な解析のためには、こちら (訳注: 原文ママ) を見てください。

 

以上



TensorFlow 2.0 Beta : Tutorials : テキストとシークエンス :- 映画レビューでテキスト分類

TensorFlow 2.0 Beta : Beginner Tutorials : テキストとシークエンス :- 映画レビューでテキスト分類 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- 映画レビューでテキスト分類

このノートブックは (映画) レビューのテキストを使用して映画レビューを肯定的か否定的として分類します。これは二値 — あるいは 2 クラス — 分類の例で、重要で広く利用可能な種類の機械学習問題です。

私達は IMDB データセット を使用します、これは Internet Movie Database からの 50,000 映画レビューのテキストを含みます。これらは訓練のための 25,000 レビューとテストのための 25,000 レビューに分割されます。訓練とテストセットは均等です、つまりそれらがポジティブとネガティブ・レビューの同じ数を含むことを意味します。

この notebook は tf.keras を使用します、TensorFlow でモデルを構築して訓練するための高位 API です。tf.keras を使用したより進んだテキスト分類チュートリアルについては、MLCC テキスト分類ガイド を見てください。

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
from tensorflow import keras

import numpy as np

print(tf.__version__)
2.0.0-beta1

 

IMDB データセットをダウンロードする

IMDB データセットは TensorFlow でパッケージ化されています。それは既にレビュー (単語のシークエンス) が整数のシークエンスに変換されるように前処理されていて、そこでは各整数は辞書の特定の単語を表しています。

次のコードは IMDB データセットを貴方のマシンにダウンロードします (あるいは既にそれをダウンロードしているのであればキャッシュされたコピーを使用します) :

imdb = keras.datasets.imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step

引数 num_words=10000 は訓練データにおいて最も頻度高く出現する top 10,000 の単語を保持します。データのサイズを管理可能に保つために稀な単語は捨てられます。

 

データを調査する

データのフォーマットを理解するために少し時間をつかいましょう。データセットは前処理されています : 各サンプルは映画レビューの単語を表わす数字の配列です。各ラベルは 0 か 1 の整数値で、そこでは 0 は否定的なレビューで、1 は肯定的なレビューです。

print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels)))
Training entries: 25000, labels: 25000

レビューのテキストは整数に変換され、そこでは各整数は辞書の特定の単語を表します。最初のレビューがどのように見えるかがここにあります :

print(train_data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

映画レビューは異なる長さかもしれません。下のコードは最初と 2 番目のレビューの単語の数を示します。ニューラルネットワークへの入力は同じ長さでなければなりませんので、これを後で解決する必要があります。

len(train_data[0]), len(train_data[1])
(218, 189)

 

整数を単語に変換し戻す

整数をどのようにテキストに変換し戻すかを知ることは有用かもしれません。ここで、整数から文字列へのマッピングを含む辞書オブジェクトに問い合わせるためのヘルパー関数を作成します :

# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()

# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()}
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2  # unknown
word_index["<UNUSED>"] = 3

reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

def decode_review(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb_word_index.json
1646592/1641221 [==============================] - 0s 0us/step

今では最初のレビューのテキストを表示するために decode_review 関数を使用することができます :

decode_review(train_data[0])
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"

 

データを準備する

レビュー — 整数の配列 — はニューラルネットワークに供給される前に tensor に変換されなければなりません。この変換は 2 つの方法で成されます :

  • 配列を単語の出現を示す 0 と 1 のベクトルに変換します、one-hot エンコーディングに類似しています。例えば、シークエンス [3, 5] は、(1 である) インデックス 3 と 5 を除いて総てゼロの 10,000 次元ベクトルになるでしょう。それから、これをネットワークの最初の層 — Dense 層 — にします、これは浮動小数点ベクトルデータを処理できます。けれども、このアプローチはメモリ集約的で、num_words * num_reviews サイズ行列を必要とします。
  • 代わりに、配列をそれらが総て同じ長さを持つようにパッドすることもできます、それから shape num_examples * max_length の整数 tensor を作成します。この shape をネットワークの最初の層として扱う埋め込み層を使用できます。

このチュートリアルでは、2 番目のアプローチを使用します。

映画レビューは同じ長さでなければならないので、長さを標準化するために pad_sequences 関数を使用します :

train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index[""],
                                                        padding='post',
                                                        maxlen=256)

test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index[""],
                                                       padding='post',
                                                       maxlen=256)

さてサンプルの長さを見てみましょう :

len(train_data[0]), len(train_data[1])
(256, 256)

そして (今はパッドされた) 最初のレビューを調査します :

print(train_data[0])
[   1   14   22   16   43  530  973 1622 1385   65  458 4468   66 3941
    4  173   36  256    5   25  100   43  838  112   50  670    2    9
   35  480  284    5  150    4  172  112  167    2  336  385   39    4
  172 4536 1111   17  546   38   13  447    4  192   50   16    6  147
 2025   19   14   22    4 1920 4613  469    4   22   71   87   12   16
   43  530   38   76   15   13 1247    4   22   17  515   17   12   16
  626   18    2    5   62  386   12    8  316    8  106    5    4 2223
 5244   16  480   66 3785   33    4  130   12   16   38  619    5   25
  124   51   36  135   48   25 1415   33    6   22   12  215   28   77
   52    5   14  407   16   82    2    8    4  107  117 5952   15  256
    4    2    7 3766    5  723   36   71   43  530  476   26  400  317
   46    7    4    2 1029   13  104   88    4  381   15  297   98   32
 2071   56   26  141    6  194 7486   18    4  226   22   21  134  476
   26  480    5  144   30 5535   18   51   36   28  224   92   25  104
    4  226   65   16   38 1334   88   12   16  283    5   16 4472  113
  103   32   15   16 5345   19  178   32    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0]

 

モデルを構築する

ニューラルネットワークは層をスタックすることにより作成されます — これは 2 つの主要なアーキテクチャ的な決定を必要とします :

  • モデルで幾つの層を使用するか?
  • 各層のために幾つの隠れユニットを使用するか?

このサンプルでは、入力データは単語インデックスの配列から成ります。予測するラベルは 0 か 1 です。この問題に対するモデルを構築しましょう :

# input shape is the vocabulary count used for the movie reviews (10,000 words)
vocab_size = 10000

model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 16)          160000    
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 16)                272       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________

分類器を構築するために層はシーケンシャルにスタックされます :

  1. 最初の層は Embedding 層です。この層は整数エンコードされた語彙を取って各単語インデックスのための埋め込みベクトルを検索します。これらのベクトルはモデルが訓練されるときに学習されます。ベクトルは出力配列に次元を追加します。結果としての次元は (batch, sequence, embedding) です。
  2. 次に、GlobalAveragePooling1D 層は各サンプルについて sequence 次元に渡り平均することにより固定長出力ベクトルを返します。これは可能な最も単純な方法でモデルが可変長の入力を扱うことを可能にします。
  3. この固定長出力ベクトルは 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  4. 最後の層は単一の出力ノードに密に接続されています。sigmoid 活性化関数を使用し、この値は 0 と 1 の間の浮動小数点で、確率、または確信レベルを表します。

 

隠れユニット

上のモデルは入力と出力の間に、2 つの中間層あるいは「隠れ」層を持ちます。出力 (ユニット、ノード、またはニューロン) の数は層のための具象空間の次元です。換言すれば、内部表現を学習するときにネットワークが許容される自由度の総量です。

モデルがより多くの隠れユニット (より高い次元の表現空間) and/or より多くの層を持てば、ネットワークはより複雑な表現を学習できます。けれども、それはネットワークをより計算的に高価にして望まないパターンを学習することに繋がるかもしれません — このパターンは訓練データ上の性能を改善しますがテストデータ上ではそうではないものです。これは overfitting と呼ばれ、後でそれを調査します。

 

損失関数と optimizer

モデルは訓練のために損失関数と optimizer を必要とします。これは二値分類問題でモデルは確率を出力します (sigmoid 活性を持つシングルユニット層) ので、binary_crossentropy 損失関数を使用します。

これは損失関数のための唯一の選択ではありません、例えば、mean_squared_error を選択できるでしょう。しかし、一般的に、binary_crossentropy は確率を扱うためにはより良いです — それは確率分布間、あるいは私達のケースでは、正解の分布と予測の間の「距離」を測ります。

後で、回帰問題 (例えば、家の価格を予測する) を調べているときに、mean squared error と呼ばれるもう一つの損失関数をどのように使用するかを見ます。

さて、optimizer と損失関数を使用するためにモデルを configure します :

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

 

検証セットを作成する

訓練時、モデルの精度をそれが前に見ていないデータ上でチェックすることを望みます。元の訓練データから 10,000 サンプルを分離することによって検証セットを作成します。(テストセットを何故今使わないのでしょう?目標は訓練データのみを用いてモデルを開発して調整することですから、テストデータは精度を評価するためだけに一度だけ使用します。)

x_val = train_data[:10000]
partial_x_train = train_data[10000:]

y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]

 

モデルを訓練する

モデルを 512 サンプルのミニバッチで 40 エポック訓練します。これは x_train と y_train tensor の総てのサンプルに渡る 40 iteration (反復) です。訓練の間、検証セットからの 10,000 サンプル上でモデルの損失と精度を監視します :

history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=40,
                    batch_size=512,
                    validation_data=(x_val, y_val),
                    verbose=1)
WARNING: Logging before flag parsing goes to stderr.
W0628 04:24:41.652144 139848380724992 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 15000 samples, validate on 10000 samples
Epoch 1/40
15000/15000 [==============================] - 1s 72us/sample - loss: 0.6923 - accuracy: 0.5162 - val_loss: 0.6910 - val_accuracy: 0.5289
Epoch 2/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.6879 - accuracy: 0.5706 - val_loss: 0.6849 - val_accuracy: 0.5642
Epoch 3/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.6778 - accuracy: 0.6386 - val_loss: 0.6721 - val_accuracy: 0.6729
Epoch 4/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.6591 - accuracy: 0.6863 - val_loss: 0.6503 - val_accuracy: 0.7282
Epoch 5/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.6304 - accuracy: 0.7521 - val_loss: 0.6191 - val_accuracy: 0.7656
Epoch 6/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.5897 - accuracy: 0.8036 - val_loss: 0.5782 - val_accuracy: 0.7876
Epoch 7/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.5414 - accuracy: 0.8253 - val_loss: 0.5318 - val_accuracy: 0.8198
Epoch 8/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.4918 - accuracy: 0.8471 - val_loss: 0.4883 - val_accuracy: 0.8327
Epoch 9/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.4452 - accuracy: 0.8619 - val_loss: 0.4489 - val_accuracy: 0.8435
Epoch 10/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.4037 - accuracy: 0.8742 - val_loss: 0.4163 - val_accuracy: 0.8519
Epoch 11/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.3685 - accuracy: 0.8827 - val_loss: 0.3894 - val_accuracy: 0.8598
Epoch 12/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.3391 - accuracy: 0.8911 - val_loss: 0.3689 - val_accuracy: 0.8631
Epoch 13/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.3150 - accuracy: 0.8971 - val_loss: 0.3510 - val_accuracy: 0.8699
Epoch 14/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2937 - accuracy: 0.9024 - val_loss: 0.3380 - val_accuracy: 0.8729
Epoch 15/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2760 - accuracy: 0.9074 - val_loss: 0.3270 - val_accuracy: 0.8744
Epoch 16/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2603 - accuracy: 0.9125 - val_loss: 0.3184 - val_accuracy: 0.8755
Epoch 17/40
15000/15000 [==============================] - 1s 52us/sample - loss: 0.2459 - accuracy: 0.9171 - val_loss: 0.3111 - val_accuracy: 0.8786
Epoch 18/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2331 - accuracy: 0.9219 - val_loss: 0.3048 - val_accuracy: 0.8807
Epoch 19/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2217 - accuracy: 0.9238 - val_loss: 0.2998 - val_accuracy: 0.8809
Epoch 20/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2114 - accuracy: 0.9277 - val_loss: 0.2964 - val_accuracy: 0.8820
Epoch 21/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.2009 - accuracy: 0.9331 - val_loss: 0.2935 - val_accuracy: 0.8829
Epoch 22/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1922 - accuracy: 0.9361 - val_loss: 0.2905 - val_accuracy: 0.8844
Epoch 23/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1834 - accuracy: 0.9408 - val_loss: 0.2895 - val_accuracy: 0.8835
Epoch 24/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1758 - accuracy: 0.9441 - val_loss: 0.2885 - val_accuracy: 0.8840
Epoch 25/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1681 - accuracy: 0.9469 - val_loss: 0.2866 - val_accuracy: 0.8855
Epoch 26/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1611 - accuracy: 0.9498 - val_loss: 0.2875 - val_accuracy: 0.8854
Epoch 27/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1546 - accuracy: 0.9519 - val_loss: 0.2868 - val_accuracy: 0.8853
Epoch 28/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1483 - accuracy: 0.9549 - val_loss: 0.2879 - val_accuracy: 0.8849
Epoch 29/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1428 - accuracy: 0.9575 - val_loss: 0.2888 - val_accuracy: 0.8845
Epoch 30/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1371 - accuracy: 0.9593 - val_loss: 0.2887 - val_accuracy: 0.8871
Epoch 31/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1312 - accuracy: 0.9619 - val_loss: 0.2896 - val_accuracy: 0.8862
Epoch 32/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1261 - accuracy: 0.9649 - val_loss: 0.2912 - val_accuracy: 0.8862
Epoch 33/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1211 - accuracy: 0.9659 - val_loss: 0.2935 - val_accuracy: 0.8851
Epoch 34/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.1166 - accuracy: 0.9670 - val_loss: 0.2960 - val_accuracy: 0.8847
Epoch 35/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1124 - accuracy: 0.9683 - val_loss: 0.2980 - val_accuracy: 0.8855
Epoch 36/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1079 - accuracy: 0.9699 - val_loss: 0.2998 - val_accuracy: 0.8840
Epoch 37/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1036 - accuracy: 0.9721 - val_loss: 0.3027 - val_accuracy: 0.8838
Epoch 38/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.0996 - accuracy: 0.9734 - val_loss: 0.3062 - val_accuracy: 0.8832
Epoch 39/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.0965 - accuracy: 0.9743 - val_loss: 0.3101 - val_accuracy: 0.8823
Epoch 40/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.0922 - accuracy: 0.9762 - val_loss: 0.3129 - val_accuracy: 0.8825

 

モデルを評価する

そしてモデルどのように遂行するか見ましょう。2 つの値が返されます。損失 (エラーを表わす数字です、より低ければより良いです)、そして精度です。

results = model.evaluate(test_data, test_labels)

print(results)
25000/25000 [==============================] - 1s 35us/sample - loss: 0.3338 - accuracy: 0.8712
[0.3338048003101349, 0.87124]

このかなり素朴なアプローチは約 87 % の精度を得ます。より進んだアプローチでは、モデルは 95 % に近づくはずです。

 

時間とともに精度と損失のグラフを作成する

model.fit() は History オブジェクトを返します、これは訓練の間に発生した総てを持つ辞書を含みます :

history_dict = history.history
history_dict.keys()
dict_keys(['val_loss', 'accuracy', 'val_accuracy', 'loss'])

4 つのエントリがあります: 訓練と検証の間に各々監視されたメトリックのために一つ (ずつ) です。比較のために訓練と検証精度に加えて、訓練と検証損失をプロットするためにこれらを使用することができます :

import matplotlib.pyplot as plt

acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
<Figure size 640x480 with 1 Axes>
plt.clf()   # clear figure

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()

plt.show()

このプロットでは、点線は訓練損失と精度を表し、そして実線は検証損失と精度です。

訓練損失は各エポックとともに減少して訓練精度は各エポックとともに増加することに気がつくでしょう。これは勾配降下最適化を使用するときに期待されるものです — それは総ての反復で望まれる量を最小化するはずです。

これは検証損失と精度については当てはまりません — それらは約 20 エポック後に最大になるようです。これは overfitting の例です : モデルは、それが前に決して見ていないデータ上よりも訓練データ上でより上手く遂行します。このポイント後、モデルは過剰に最適化されてテストデータに一般化されない訓練データに固有の表現を学習します。

この特定のケースのためには、単純に 20 程度のエポック後に訓練を停止することで overfitting を回避できるでしょう。後で、これを callback で自動的にどのように行なうかを見るでしょう。

 

以上



TensorFlow 機械学習ガイド : テキスト分類 (5) ハイパーパラメータの調整、モデルの配備、バッチ訓練

TensorFlow 機械学習ガイド : テキスト分類 (5) ハイパーパラメータの調整、モデルの配備、バッチ訓練 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/23/2018

* 本ページは、developers.google.com サイトの Machine Learning Guides : Text classification の以下のページを
翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Step 5: ハイパーパラメータを調整する

モデルを定義して訓練するためにハイパーパラメータの数字を選択しなければなりませんでした。直感、例そしてベストプラクティス推奨に依拠しました。けれども、ハイパーパラメータ値の最初の選択は最善の結果を産まないかもしれません。それは訓練のための良い開始点を与えてくれるだけです。総ての問題は異なり、これらのハイパーパラメータの調整は手元の問題の特殊性をより良く表わすためにモデルを改良することに役立ちます。使用したハイパーパラメータの幾つかとそれらの調整が意味するものを見てみましょう :

  • モデルの層数: ニューラルネットワークの層の数はその複雑さの指標です。この値を選択する際に慎重でなければなりません。多すぎる層はモデルに訓練データについて多過ぎる情報を学習することを可能にし、overfitting を引き起こします。少な過ぎる層はモデルの学習能力を制限することができて、underfitting を引き起こします。テキスト分類データセットのためには、1, 2, そして 3 層 MLP で実験しました。2 層を持つモデルは上手く遂行し、ある場合には 3 層モデルよりもより良かったです。同様に、4 と 6 層を持つ sepCNN を試し、4 層モデルが上手く遂行しました。
  • 層毎のユニット数: 層のユニットは層が遂行する変換のための情報を保持しなければなりません。最初の層については、これは特徴数により駆動されます。続く層内では、ユニット数は前の層からの表現を拡大するか縮小するかの選択に依拠します。層間の情報損失を最小化してみます。範囲 [8, 16, 32, 64] のユニット数を試して、32/64 ユニットが上手く動作しました。
  • Dropout 比率: Dropout 層は正則化のためにモデルで使用されます。それらは overfitting のための予防として drop する入力の比率を定義します。推奨範囲: 0.2–0.5 です。
  • 学習率: これらはニューラルネットワーク重みが反復間で変更する比率です。巨大な学習率は重みの巨大な変動を引き起こすかもしれません、そしてそれらの最適な値を決して見つけられないかもしれません。低い学習率は良いですが、モデルは収束するためにより多くの反復を取るでしょう。低く始めるのは良い考えです、例えば 1e-4 です。訓練が非常に遅いのであれば、この値を増やします。モデルが学習してないのであれば、学習率を減少してみてください。

 
調整した 2 つの追加のハイパーパラメータがあります、これは sepCNN モデルに固有のものです :

  1. カーネル・サイズ: 畳み込みウィンドウのサイズです。推奨値: 3 または 5 です。
  2. 埋め込み次元: 単語埋め込みを表わすために使用することを望む次元数です — i.e., 各単語ベクトルのサイズです。推奨値: 50-300 です。実験では、事前訓練された埋め込み層を持つ 200 次元を持つ GloVe 埋め込みを使用しました。

これらのハイパーパラメータで遊んでみて何が最善に動作するかを見てください。ひとたび貴方のユースケースについて最もパフォーマンスの良いパラメータを選択すれば、貴方のモデルは配備される準備ができました。

 

Step 6: モデルを配備する

Google Cloud 上で機械学習モデルを訓練、調整そして配備することができます。プロダクションのために貴方のモデルを配備する際のガイダンスについて以下のリソースを見てください :

貴方のモデルを配備するとき以下の主要なことに留意してください :

  • プロダクション・データは訓練と評価データと同じ分布に従ってください。
  • 定期的により多くの訓練データを集めて再評価してください。
  • データ分布が変われば、モデルを再訓練してください。

 

おわりに

テキスト分類は様々なプロダクションに渡るアプリケーションに伴う基礎的な機械学習問題です。このガイドでは、テキスト分類のワークフローを幾つかのステップに分解しました。各ステップについて、貴方の特定のデータセット特性に基づいてカスタマイズしたアプローチを提案してきました。特に、number of samples の number of words per sample に対する比率を利用して、ベスト・パフォーマンスに迅速に近づけるモデル・タイプを提案します。他のステップはこの選択まわりに設計されています。このガイドに従って、付随するコード、そしてフローチャートが貴方が学習し、理解し、そして貴方のテキスト分類問題への素早い素案としてのソリューションを得る助けとなることを望みます。

 

Appendix: バッチ訓練

非常に巨大なデータセットはプロセスに割り当てられたメモリに収まらないかもしれません。前のステップでは、パイプラインを設定しました、そこではデータセット全体をメモリに持ち込み、データを準備して、そして訓練関数に作業用セットを渡します。代わりに、Keras はバッチでデータを取得する、代わりの訓練関数 (fit_generator) を提供します。これはデータ・パイプラインの変換をデータの小さい (batch_size の倍数) 部分に適用することを可能にします。実験の間、DBPedia, Amazon レビュー, Ag ニュース, そして Yelp レビューのようなデータセットのためにバッチ処理を使用しました。

次のコードはどのようにデータバッチを生成してそれらを fit_generator に供給するかを示します。

def _data_generator(x, y, num_features, batch_size):
    """Generates batches of vectorized texts for training/validation.

    # Arguments
        x: np.matrix, feature matrix.
        y: np.ndarray, labels.
        num_features: int, number of features.
        batch_size: int, number of samples per batch.

    # Returns
        Yields feature and label data in batches.
    """
    num_samples = x.shape[0]
    num_batches = num_samples // batch_size
    if num_samples % batch_size:
        num_batches += 1

    while 1:
        for i in range(num_batches):
            start_idx = i * batch_size
            end_idx = (i + 1) * batch_size
            if end_idx > num_samples:
                end_idx = num_samples
            x_batch = x[start_idx:end_idx]
            y_batch = y[start_idx:end_idx]
            yield x_batch, y_batch

# Create training and validation generators.
training_generator = _data_generator(
    x_train, train_labels, num_features, batch_size)
validation_generator = _data_generator(
    x_val, val_labels, num_features, batch_size)

# Get number of training steps. This indicated the number of steps it takes
# to cover all samples in one epoch.
steps_per_epoch = x_train.shape[0] // batch_size
if x_train.shape[0] % batch_size:
    steps_per_epoch += 1

# Get number of validation steps.
validation_steps = x_val.shape[0] // batch_size
if x_val.shape[0] % batch_size:
    validation_steps += 1

# Train and validate model.
history = model.fit_generator(
    generator=training_generator,
    steps_per_epoch=steps_per_epoch,
    validation_data=validation_generator,
    validation_steps=validation_steps,
    callbacks=callbacks,
    epochs=epochs,
    verbose=2)  # Logs once per epoch.
 

以上



TensorFlow 機械学習ガイド : テキスト分類 (4) モデルの構築、訓練そして評価

TensorFlow 機械学習ガイド : テキスト分類 (4) モデルの構築、訓練そして評価 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/23/2018

* 本ページは、developers.google.com サイトの Machine Learning Guides : Text classification の以下のページを
翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

本文

このセクションでは、モデルの構築、訓練そして評価に向けて作業します。Step 3 では、S/W 比率を使用して、n-gram モデルかシークエンス・モデルを使用するかを選択しています。今、分類器アルゴリズムを書いてそれを訓練するときです。私達はこのために tf.keras API を伴う TensorFlow を使用します。

Keras で機械学習モデルを構築することは層、データ処理ビルディングブロックを一緒に組み立てることが総てで、Lego ブロックを組み立てるようなものです。これらの層は入力上で遂行することを望む変換のシークエンスを指定することを可能にします。私達の学習アルゴリズムは単一のテキスト入力を取り単一の分類を出力しますので、Sequential モデル API を使用して層の線形スタックを作成することができます。

Figure 9: 層の線形スタック

入力層と中間層は、n-gram からシークエンス・モデルを構築するかに依拠して、異なる構成が成されます。しかしモデルのタイプにかかわりなく、最後の層は与えられた問題に対して同じです。

 

最後の層を構築する

2 クラスだけを持つ (二値分類) とき、モデルは単一の確率スコアを出力すべきです。例えば、与えられたサンプルに対して 0.2 の出力は「このサンプルがクラス 0 である 20% の信頼度とクラス 1 である 80 % の信頼度」を意味します。そのような確率スコアを出力するためには、最後の層の活性化関数は sigmoid 関数であるべきで、モデルを訓練するために使用される損失関数は binary cross-entropy (二値交差エントロピー) であるべきです (Figure10, 左側参照)。

2 クラス以上ある (多クラス分類) ときは、モデルはクラス毎に一つの確率スコアを出力するべきです。スコアの総計は 1 であるはずです。例えば、{0: 0.2, 1: 0.7, 2: 0.1} の出力は、「このサンプルがクラス 0 である 20% の信頼度、クラス 1 である 70% の信頼度、そしてクラス 2 である 10 % の信頼度」を意味します。これらのスコアを出力するためには、最後の層の活性化関数は softmax であるべきで、モデルを訓練するために使用される損失関数は categorical cross-entropy (カテゴリカル交差エントロピー) であるべきです (Figure 10, 右側参照)。

Figure 10: 最後の層

 
次のコードは関数を定義します。入力としてクラス数を取り、適切な層ユニットの数 (二値分類のために 1 ユニット; そうでなければ各クラスのために 1 ユニット) と適切な活性化関数を出力します :

def _get_last_layer_units_and_activation(num_classes):
    """Gets the # units and activation function for the last network layer.

    # Arguments
        num_classes: int, number of classes.

    # Returns
        units, activation values.
    """
    if num_classes == 2:
        activation = 'sigmoid'
        units = 1
    else:
        activation = 'softmax'
        units = num_classes
    return units, activation

 
次の 2 つのセクションは n-gram モデルとシークエンス・モデルに対する残りのモデル層の作成をウォークスルーします。

S/W 比率が小さいとき、n-gram モデルはシークエンス・モデルよりもより良く遂行することを私達は見出しました。シークエンス・モデルは巨大な数の小さい、密ベクトルがあるときにより良いです。これは埋め込み関係が密空間で学習されて、これは多くのサンプルに渡りベストに発生するからです。

 

n-gram モデルを構築する [オプション A]

トークンを独立的に (単語順序を考慮しないで) 処理するモデルを n-gram モデルとして参照します。(ロジスティック回帰を含む) 単純な多層パーセプトロン、勾配ブースティングマシンとサポートベクターマシンは総てこのカテゴリに入ります ; それらはテキスト順序についてのどのような情報も活用しません。

上で言及した n-gram モデルの幾つかのパフォーマンスを比較して他の選択肢よりも 多層パーセプトロン (MLP) が良く遂行する ことを観察しました。MLP は定義して理解するために単純で、良い精度を提供し、そして比較的小さい計算を必要とします。

次のコードは 2-層 MLP モデルを tf.keras で定義し、正則化のために 2 つの Dropout 層を追加しています (訓練サンプルへの overfitting を防ぐため)。

from tensorflow.python.keras import models
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import Dropout

def mlp_model(layers, units, dropout_rate, input_shape, num_classes):
    """Creates an instance of a multi-layer perceptron model.

    # Arguments
        layers: int, number of `Dense` layers in the model.
        units: int, output dimension of the layers.
        dropout_rate: float, percentage of input to drop at Dropout layers.
        input_shape: tuple, shape of input to the model.
        num_classes: int, number of output classes.

    # Returns
        An MLP model instance.
    """
    op_units, op_activation = _get_last_layer_units_and_activation(num_classes)
    model = models.Sequential()
    model.add(Dropout(rate=dropout_rate, input_shape=input_shape))

    for _ in range(layers-1):
        model.add(Dense(units=units, activation='relu'))
        model.add(Dropout(rate=dropout_rate))

    model.add(Dense(units=op_units, activation=op_activation))
    return model

 

シークエンス・モデルを構築する [オプション B]

トークンの隣接から学習可能なモデルをシークエンス・モデルとして参照します。これはモデルの CNN と RNN クラスを含みます。データはこれらのモデルのためにシークエンス・ベクトルとして前処理されます。

シークエンス・モデルは学習するためのより巨大なパラメータ数を一般的に持ちます。これらのモデルの最初の層は埋め込み層で、これは密ベクトル空間で単語間の関係を学習します。単語関係の学習は多くのサンプルに渡りベストに動作します。

与えられたデータセットの単語はおそらくそのデータセットに固有ではないでしょう。そのため他のデータセットを使用して私達のデータセットの単語間の関係を学習できます。それをするために、他のデータセットから学習した埋め込みを私達の埋め込み層に転移する (= transfer) ことができます。これらの埋め込みは事前訓練された埋め込みとして参照されます。事前訓練された埋め込みの使用は学習過程においてモデルに有利なスタートを与えます。

GloVe のように、巨大なコーパスを使用して訓練された利用可能な事前訓練された埋め込みがあります。GloVe は複数のコーパス (主として Wikipedia) 上で訓練されました。GloVe 埋め込みのバージョンを使用して私達のシークエンス・モデルを訓練することをテストし、事前訓練された埋め込みの重みを凍結してネットワークの残りだけを訓練した場合、モデルは上手く遂行しないことを私達は観察しました。これは埋め込み層が訓練されたコンテキストが、それを使用しているコンテキストとは異なるためでしょう。

Wikipedia データ上で訓練された GloVe 埋め込みは IMDb データセットの言語パターンと合致しません。推定される関係は何某かの更新が必要かもしれません — i.e., 埋め込み重みはコンテキスト的な調整が必要かもしれません。これを 2 ステージで行ないます :

  1. 最初の実行では、埋め込み層の重みは凍結して、ネットワークの残りに学習することを許容します。この実行の最後には、モデル重みは非初期化値よりも遥かに良い状態に達します。2 番目の実行では、埋め込み層にもまた学習することを許容し、ネットワークの総ての重みに微調整 (= fine adjustments) を行ないます。この過程を再調整された埋め込みの使用として参照します。
  2. 再調整された埋め込みはより良い精度を生成します。けれども、これはネットワークを訓練するために必要な増大する計算パワーと引き換えに生じています。十分な数のサンプルが与えられたとき、スクラッチからでも上手く埋め込みを学習できるでしょう。S/W > 15K については、スクラッチから開始しても再調整された埋め込みとおよそ同じ精度を効果的に生成します。

私達はモデル・アーキテクチャを多様にして、CNN, sepCNN (Depthwise Separable Convolutional Network), RNN (LSTM & GRU), CNN-RNN, そして stacked RNN のような異なるシークエンス・モデルを比較しました。 しばしばよりデータ効率的で計算効率的な畳み込みネットワークの変種 – sepCNN が他のモデルよりもより良く遂行することを見出しました。

Note: RNN はユースケースの小さいサブセットだけに関連します。QRNN や RNNs with Attention のようなモデルは試していません、何故ならばそれらの精度改善はより高い計算コストによる補正だからです。

次のコードは 4 層 sepCNN モデルを構築します :

from tensorflow.python.keras import models
from tensorflow.python.keras import initializers
from tensorflow.python.keras import regularizers

from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import Dropout
from tensorflow.python.keras.layers import Embedding
from tensorflow.python.keras.layers import SeparableConv1D
from tensorflow.python.keras.layers import MaxPooling1D
from tensorflow.python.keras.layers import GlobalAveragePooling1D

def sepcnn_model(blocks,
                 filters,
                 kernel_size,
                 embedding_dim,
                 dropout_rate,
                 pool_size,
                 input_shape,
                 num_classes,
                 num_features,
                 use_pretrained_embedding=False,
                 is_embedding_trainable=False,
                 embedding_matrix=None):
    """Creates an instance of a separable CNN model.

    # Arguments
        blocks: int, number of pairs of sepCNN and pooling blocks in the model.
        filters: int, output dimension of the layers.
        kernel_size: int, length of the convolution window.
        embedding_dim: int, dimension of the embedding vectors.
        dropout_rate: float, percentage of input to drop at Dropout layers.
        pool_size: int, factor by which to downscale input at MaxPooling layer.
        input_shape: tuple, shape of input to the model.
        num_classes: int, number of output classes.
        num_features: int, number of words (embedding input dimension).
        use_pretrained_embedding: bool, true if pre-trained embedding is on.
        is_embedding_trainable: bool, true if embedding layer is trainable.
        embedding_matrix: dict, dictionary with embedding coefficients.

    # Returns
        A sepCNN model instance.
    """
    op_units, op_activation = _get_last_layer_units_and_activation(num_classes)
    model = models.Sequential()

    # Add embedding layer. If pre-trained embedding is used add weights to the
    # embeddings layer and set trainable to input is_embedding_trainable flag.
    if use_pretrained_embedding:
        model.add(Embedding(input_dim=num_features,
                            output_dim=embedding_dim,
                            input_length=input_shape[0],
                            weights=[embedding_matrix],
                            trainable=is_embedding_trainable))
    else:
        model.add(Embedding(input_dim=num_features,
                            output_dim=embedding_dim,
                            input_length=input_shape[0]))

    for _ in range(blocks-1):
        model.add(Dropout(rate=dropout_rate))
        model.add(SeparableConv1D(filters=filters,
                                  kernel_size=kernel_size,
                                  activation='relu',
                                  bias_initializer='random_uniform',
                                  depthwise_initializer='random_uniform',
                                  padding='same'))
        model.add(SeparableConv1D(filters=filters,
                                  kernel_size=kernel_size,
                                  activation='relu',
                                  bias_initializer='random_uniform',
                                  depthwise_initializer='random_uniform',
                                  padding='same'))
        model.add(MaxPooling1D(pool_size=pool_size))

    model.add(SeparableConv1D(filters=filters * 2,
                              kernel_size=kernel_size,
                              activation='relu',
                              bias_initializer='random_uniform',
                              depthwise_initializer='random_uniform',
                              padding='same'))
    model.add(SeparableConv1D(filters=filters * 2,
                              kernel_size=kernel_size,
                              activation='relu',
                              bias_initializer='random_uniform',
                              depthwise_initializer='random_uniform',
                              padding='same'))
    model.add(GlobalAveragePooling1D())
    model.add(Dropout(rate=dropout_rate))
    model.add(Dense(op_units, activation=op_activation))
    return model

 

モデルを訓練する

モデル・アーキテクチャを構築した今、モデルを訓練する必要があります。訓練はモデルの現在の状態に基づいて予測を行ない、予測がどのように正しくないかを計算し、そしてこのエラーを最小化してモデルをより良く予測させるためにネットワークの重みあるいはパラメータを更新することを伴います。このプロセスをモデルが収束してもはや学習できなくなるまで繰り返します。このプロセスのために選択される 3 つの主要なパラメータがあります (Table 2 参照)。

  • メトリック: メトリックを使用してモデルのパフォーマンスをどのように測定するか。私達の実験では accracy (精度) をメトリックとして使用しました。
  • 損失関数: 訓練プロセスがネットワーク重みを調整することにより最小化しようと試みる損失値を計算するために使用される関数。分類問題に対しては、cross-entropy 損失が上手く動作します。
  • Optimizer: 損失関数の出力に基づいてどのようにネットワーク重みが更新されるかを決定する関数です。私達の実験では一般的な Adam optimizer を使用しました。 

Keras では、これらの学習パラメータを compile メソッドを使用してモデルに渡します。

学習パラメータ
Metric accuracy
損失関数 – binary classification binary_crossentropy
損失関数 – multi class classification sparse_categorical_crossentropy
Optimizer adam

Table 2: 学習パラメータ

実際の訓練は fit メソッドの使用で発生します。データセットのサイズに依拠して、これは殆どの計算サイクルがそこで費やされるメソッドです。各訓練反復で、訓練データから batch_size 数のサンプルが損失を計算するために使用され、この値を基に重みが一度だけ更新されます。モデルが訓練データセット全体を一度見れば訓練プロセスは (1 つの) エポックを完了します。各エポックの最後に、モデルがどのように学習しているかを評価するために検証データセットを使用します。規定のエポック数の間データセットを使用して訓練を繰り返します。これを stopping early によって最適化しても良いです、検証精度が連続するエポック間で安定するとき、モデルはもはや訓練されないことを示します。

訓練ハイパーパラメータ
Learning rate 1e-3
Epochs 1000
Batch size 512
Early stopping parameter: val_loss, patience: 1

Table 3: 訓練ハイパーパラメータ

次の Keras コードは上の Table 2 & 3 で選択されたパラメータを使用して訓練プロセスを実装します :

def train_ngram_model(data,
                      learning_rate=1e-3,
                      epochs=1000,
                      batch_size=128,
                      layers=2,
                      units=64,
                      dropout_rate=0.2):
    """Trains n-gram model on the given dataset.

    # Arguments
        data: tuples of training and test texts and labels.
        learning_rate: float, learning rate for training model.
        epochs: int, number of epochs.
        batch_size: int, number of samples per batch.
        layers: int, number of `Dense` layers in the model.
        units: int, output dimension of Dense layers in the model.
        dropout_rate: float: percentage of input to drop at Dropout layers.

    # Raises
        ValueError: If validation data has label values which were not seen
            in the training data.
    """
    # Get the data.
    (train_texts, train_labels), (val_texts, val_labels) = data

    # Verify that validation labels are in the same range as training labels.
    num_classes = explore_data.get_num_classes(train_labels)
    unexpected_labels = [v for v in val_labels if v not in range(num_classes)]
    if len(unexpected_labels):
        raise ValueError('Unexpected label values found in the validation set:'
                         ' {unexpected_labels}. Please make sure that the '
                         'labels in the validation set are in the same range '
                         'as training labels.'.format(
                             unexpected_labels=unexpected_labels))

    # Vectorize texts.
    x_train, x_val = vectorize_data.ngram_vectorize(
        train_texts, train_labels, val_texts)

    # Create model instance.
    model = build_model.mlp_model(layers=layers,
                                  units=units,
                                  dropout_rate=dropout_rate,
                                  input_shape=x_train.shape[1:],
                                  num_classes=num_classes)

    # Compile model with learning parameters.
    if num_classes == 2:
        loss = 'binary_crossentropy'
    else:
        loss = 'sparse_categorical_crossentropy'
    optimizer = tf.keras.optimizers.Adam(lr=learning_rate)
    model.compile(optimizer=optimizer, loss=loss, metrics=['acc'])

    # Create callback for early stopping on validation loss. If the loss does
    # not decrease in two consecutive tries, stop training.
    callbacks = [tf.keras.callbacks.EarlyStopping(
        monitor='val_loss', patience=2)]

    # Train and validate model.
    history = model.fit(
            x_train,
            train_labels,
            epochs=epochs,
            callbacks=callbacks,
            validation_data=(x_val, val_labels),
            verbose=2,  # Logs once per epoch.
            batch_size=batch_size)

    # Print results.
    history = history.history
    print('Validation accuracy: {acc}, loss: {loss}'.format(
            acc=history['val_acc'][-1], loss=history['val_loss'][-1]))

    # Save model.
    model.save('IMDb_mlp_model.h5')
    return history['val_acc'][-1], history['val_loss'][-1]
 

以上



TensorFlow 機械学習ガイド : テキスト分類 (3) データの準備 (前処理)

TensorFlow 機械学習ガイド : テキスト分類 (3) データの準備 (前処理) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/22/2018

* 本ページは、developers.google.com サイトの Machine Learning Guides : Text classification の以下のページを
翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Step 3: データを準備する

データがモデルに供給できる前に、それはモデルが理解できるフォーマットに変換される必要があります。

最初に、集めたデータ・サンプルは特別な順序にあるかもしれません。テキストとラベル間の関係に影響するサンプルの順序に関連するどのような情報も望みません。例えば、データセットがクラスでソートされてから訓練/検証セットに分割されている場合、これらのセットはデータの全体的な分布の見本にはなりません。

モデルがデータ順序に影響されないことを確実にする単純なベストプラクティスは他に何かする前にデータを必ずシャッフルすることです。もしデータが既に訓練と検証セットに分割されている場合には、訓練データを変換するのと同じ方法で検証データを変換することを確実にしてください。分離した訓練と検証セットをまだ持たない場合には、シャッフル後にサンプルを分割することもできます ; 典型的には訓練のためにサンプルの 80% をそして検証のために 20% を使用します。

2 番目に、機械学習アルゴリズムは入力として数字を取ります。これはテキストを数値ベクトルに変換する必要があることを意味します。このプロセスには 2 つのステップがあります :

  1. トークン化 (=Tokenization): テキストを単語かより小さい部分テキストに分割します、これはテキストとラベル間の関係の良い一般化を可能にします。これはデータセットの「語彙 (= vocabulary)」を決定します (データに現れる一意なトークンの集合)。
  2. ベクトル化: これらのテキストを特性化するために良い数値尺度を定義します。

n-gram ベクトルとシークエンス・ベクトルの両者のためのこれら 2 つのステップをどのように遂行するか、そして特徴選択と正規化技術を使用してどのようにベクトル表現を最適化するかを見ましょう。

 

N-gram ベクトル [オプション A]

次のパラグラフでは、n-gram モデルのためのトークン化とベクトル化をどのように行なうかを見ます。特徴選択と正規化技術を使用して n-gram 表現をどのように最適化できるかもまたカバーします。

n-gram ベクトルでは、テキストは一意な n-gram: n 隣接トークン (典型的には、単語) のコレクションとして表わされます。テキスト The mouse ran up the clock を考えます。ここで、単語 unigram (n=1) は [‘the’, ‘mouse’, ‘ran’, ‘up’, ‘clock’] で、単語 bigram (n = 2) は [‘the mouse’, ‘mouse ran’, ‘ran up’, ‘up the’, ‘the clock’]、等々。

 

トークン化

単語 unigram + bigram へのトークン化が良い精度を提供する一方で、より少ない計算時間しかかからないことを私達は見出しました。

 

ベクトル化

テキスト・サンプルを n-gram にひとたび分割したならば、これらの n-gram を機械学習モデルが処理できる数値ベクトルに変える必要があります。下のサンプルは 2 つのテキストのために生成された unigram と bigram に割り当てられたインデックスを示します。

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token:
  {'the': 7, 'mouse': 2, 'ran': 4, 'up': 10, 'clock': 0, 'the mouse': 9, 'mouse ran': 3,
  'ran up': 6, 'up the': 11, 'the clock': 8, 'down': 1, 'ran down': 5}

ひとたびインデックスが n-gram に割り当てられれば、典型的には次のオプションを使用してベクトル化します。

One-hot エンコーディング: 総てのサンプルテキストはテキスト内のトークンの存在か欠如を示すベクトルとして現れます。

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]

カウント・エンコーディング: 総てのサンプルテキストはテキスト内のトークンのカウントを示すベクトルとして表わされます。unigram ‘the’ に対応する要素は 2 として表わされることに注意してください、何故ならば単語 “the” はテキスト内に 2 度現れるからです。

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1]

Tf-idf エンコーディング: 上の 2 つのアプローチが持つ問題は総ての文書で同様の頻度で現れる一般的な単語 (i.e., データセットのテキストサンプルに特に固有ではない単語) にペナルティが与えられないことです。例えば、”a” のような単語は総てのテキストで非常に頻繁に現れます。そのため他の意味のある単語よりもより高い “the” に対するトークンカウントは大して有用ではありません。

'The mouse ran up the clock' = [0.33, 0, 0.23, 0.23, 0.23, 0, 0.33, 0.47, 0.33, 0.23, 0.33, 0.33]
(Scikit-learn TdidfTransformer 参照)

多くの他のベクトル表現がありますが、上の 3 つは最も一般的に使用されます。

tf-idf エンコーディングは他の 2 つよりも精度 (平均で: 0.25-15% higher) の点で僅かにより良いことを私達は観察し、そして n-gram のベクトル化にこのメソッドを使用することを勧めます。けれども、それはより多くのメモリを占有して (何故ならばそれは浮動小数点表現を使用しますので) 計算するためにより多くの時間がかかることに留意してください、特に巨大なデータセットについて (ある場合には 2 倍の長さがかかるかもしれません)。

 

特徴選択

データセットのテキストの総てを単語 uni+bigram トークンに変換するとき、数万のトークンという結果で終わるかもしれません。これらのトークン/特徴の総てがラベル予測に寄与するわけではありません。従って、特定のトークンを破棄することができます、例えばデータセットに渡り極めて稀にしか出現しないものです。特徴の重要性を測定することもでき (各トークンがラベル予測にどの程度寄与するか)、そして最も情報を与える (= informative) トークンだけを含むことができます。

特徴と対応するラベルを取り特徴重要性スコアを出力する多くの統計関数があります。2 つの一般に使用される関数は f_classifchi2 です。私達の実験はこれらの関数の両者が同等に良く遂行することを示します。

より重要なことは、多くのデータセットに対して精度は 20,000 特徴あたりでピークに達します (Figure 6 参照)。この閾値を越えてより多くの特徴を追加しても殆ど寄与せずそして時に overfitting に繋がってパフォーマンスが低下さえします。

★ ここでのテストでは英語テキストだけを使用しました。特徴の理想的な数は言語により変化するかもしれません ; これは補足解析で調査されるでしょう。

Figure 6: Top K 特徴 vs 精度。データセットに渡り、精度は top 20K 特徴あたりで平坦化します。

 

正規化

正規化は総ての特徴/サンプルの値を小さくて類似の値に変換します。これは学習アルゴリズムにおける勾配降下の収束を単純化します。私達が見たことからは、データ前処理の間の正規化はテキスト分類問題において大した値を追加しないように見えます ; このステップをスキップすることを勧めます。

 
◆ 次のコードは上のステップの総てをまとめています :

  • テキスト・サンプルを単語 uni+bigram にトークン化し、
  • tf-idf エンコーディングでベクトル化して、
  • 2 回より少なく出現するトークンを捨てて特徴重要性を計算するために f_classif を使用することによりトークンのベクトルから top 20,000 特徴だけを選択します。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif

# Vectorization parameters
# Range (inclusive) of n-gram sizes for tokenizing text.
NGRAM_RANGE = (1, 2)

# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Whether text should be split into word or character n-grams.
# One of 'word', 'char'.
TOKEN_MODE = 'word'

# Minimum document/corpus frequency below which a token will be discarded.
MIN_DOCUMENT_FREQUENCY = 2

def ngram_vectorize(train_texts, train_labels, val_texts):
    """Vectorizes texts as n-gram vectors.

    1 text = 1 tf-idf vector the length of vocabulary of unigrams + bigrams.

    # Arguments
        train_texts: list, training text strings.
        train_labels: np.ndarray, training labels.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val: vectorized training and validation texts
    """
    # Create keyword arguments to pass to the 'tf-idf' vectorizer.
    kwargs = {
            'ngram_range': NGRAM_RANGE,  # Use 1-grams + 2-grams.
            'dtype': 'int32',
            'strip_accents': 'unicode',
            'decode_error': 'replace',
            'analyzer': TOKEN_MODE,  # Split text into word tokens.
            'min_df': MIN_DOCUMENT_FREQUENCY,
    }
    vectorizer = TfidfVectorizer(**kwargs)

    # Learn vocabulary from training texts and vectorize training texts.
    x_train = vectorizer.fit_transform(train_texts)

    # Vectorize validation texts.
    x_val = vectorizer.transform(val_texts)

    # Select top 'k' of the vectorized features.
    selector = SelectKBest(f_classif, k=min(TOP_K, x_train.shape[1]))
    selector.fit(x_train, train_labels)
    x_train = selector.transform(x_train).astype('float32')
    x_val = selector.transform(x_val).astype('float32')
    return x_train, x_val

n-gram ベクトル表現では、単語順序と文法について多くの情報を捨てます (せいぜい、n > 1 の時にある程度の部分的な順序情報を維持できるだけです)。これは bag-of-words アプローチと呼ばれます。この表現はロジスティック回帰、多層パーセプトロン、勾配ブースティング・マシン、サポートベクターマシンのような、順序を考慮しないモデルと連携して使用されます。

 

シークエンス・ベクトル [オプション B]

次のパラグラフでは、シークエンス・モデルのためのトークン化とベクトル化をどのように行なうかを見ます。特徴選択と正規化技術を使用してシークエンス表現をどのように最適化できるかもまたカバーします。

あるテキスト・サンプルについては、単語順序はテキストの意味に対して重要です。例えば、センテンス “I used to hate my commute. My new bike changed that completely” は順序正しく読むことによってのみ理解可能です。CNN/RNN のようなモデルはサンプルの単語の順序から意味を推論できます。これらのモデルのためには、テキストを順序を保持して、トークンのシークエンスとして表します。

 

トークン化

テキストは文字のシークエンスか、単語のシークエンスとして表わされます。単語レベル表現の使用が文字トークンよりもより良いパフォーマンスを提供することを見出しました。これはまた業界で追随される一般的な規範です。文字トークンの使用はテキストが多くのタイポを持つ場合に限り意味があり、これは通常は当てはまりません。

 

ベクトル化

ひとたびテキスト・サンプルを単語のシークエンスに変換したならば、これらのシークエンスを数値ベクトルに変える必要があります。下の例は 2 つのテキストのために生成された unigram に割り当てられたインデックスと、それから最初のテキストが変換されたトークン・インデックスのシークエンスです。

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token: {'clock': 5, 'ran': 3, 'up': 4, 'down': 6, 'the': 1, 'mouse': 2}.
NOTE: 'the' occurs most frequently, so the index value of 1 is assigned to it.
Some libraries reserve index 0 for unknown tokens, as is the case here.
Sequence of token indexes: 'The mouse ran up the clock' = [1, 2, 3, 4, 1, 5]

トークン・シークエンスをベクトル化するために利用可能な 2 つのオプションがあります。

One-hot エンコーディング: シークエンスは n-次元空間の単語ベクトルを使用して表わされます、ここでn = 語彙のサイズです。この表現は文字としてトークン化しているとき、そして従って語彙が小さいときに素晴らしく動作します。単語としてトークン化しているときは、語彙は通常は数万のトークンを持ち、one-hot ベクトルを非常にスパースで非効率にします。例えば :

'The mouse ran up the clock' = [
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 0, 0],
  [0, 0, 0, 0, 1, 0, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 1, 0]
]

単語埋め込み (= Word embeddings): 単語はそれらに関係する意味を持ちます。結果として、単語トークンを密ベクトル空間 (~ 数百の実数) 内で表わすことができます、そこでは位置と単語間の距離はそれらが意味的にどのように類似しているかを示します (Figure 7 参照)。この表現は単語埋め込み (= word embeddings)と呼ばれます。

Figure 7: 単語埋め込み (= Word embeddings)

 
シークエンス・モデルは最初の層としてそのような埋め込み層をしばしば持ちます。この層は訓練プロセスの間に単語インデックス・シークエンスを単語埋め込みベクトルに変えることを学習します、各単語インデックスが意味空間の単語の位置を表わす実数の密ベクトルにマップされるようにです (Figure 8 参照)。

Figure 8: 埋め込み層 (= Embedding layer)

 

特徴選択

データの総ての単語がラベル予測に寄与するわけではありません。語彙から稀だったり重要でない単語を捨てることで学習プロセスを最適化できます。実際に、最も頻度の高い 20,000 特徴の使用が一般に十分であることを私達は観察しました。これは n-gram モデルに対しても事実です (Figure 6 参照)。

シークエンス・ベクトル化の上のステップの総てをまとめましょう。次のコードはこれらのタスクを遂行します :

  • テキストを単語にトークン化する
  • top 20,000 トークンを使用して語彙を作成する
  • トークンをシークエンス・ベクトルに変換する
  • シークエンスを固定されたシークエンス長にパッドする
from tensorflow.python.keras.preprocessing import sequence
from tensorflow.python.keras.preprocessing import text

# Vectorization parameters
# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Limit on the length of text sequences. Sequences longer than this
# will be truncated.
MAX_SEQUENCE_LENGTH = 500

def sequence_vectorize(train_texts, val_texts):
    """Vectorizes texts as sequence vectors.

    1 text = 1 sequence vector with fixed length.

    # Arguments
        train_texts: list, training text strings.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val, word_index: vectorized training and validation
            texts and word index dictionary.
    """
    # Create vocabulary with training texts.
    tokenizer = text.Tokenizer(num_words=TOP_K)
    tokenizer.fit_on_texts(train_texts)

    # Vectorize training and validation texts.
    x_train = tokenizer.texts_to_sequences(train_texts)
    x_val = tokenizer.texts_to_sequences(val_texts)

    # Get max sequence length.
    max_length = len(max(x_train, key=len))
    if max_length > MAX_SEQUENCE_LENGTH:
        max_length = MAX_SEQUENCE_LENGTH

    # Fix sequence length to max value. Sequences shorter than the length are
    # padded in the beginning and sequences longer are truncated
    # at the beginning.
    x_train = sequence.pad_sequences(x_train, maxlen=max_length)
    x_val = sequence.pad_sequences(x_val, maxlen=max_length)
    return x_train, x_val, tokenizer.word_index

 

ラベルのベクトル化

サンプルのテキストデータをどのように数値ベクトルに変換するかを見ました。同様のプロセスはラベルに適用されなければなりません。ラベルを範囲 [0, num_classes – 1] の値に単純に変換できます。例えば、3 クラスがあれば、それらを表わすために値 0, 1 と 2 を単に使用できます。内部的には、ネットワークはこれらの値を表わすために one-hot ベクトルを使用します (ラベル間の間違った関係を推定することを回避するため)。この表現はネットワークで使用する損失関数と最後の層の活性化関数に依拠します。これらについての次のセクションで更に学習します。

 

以上



TensorFlow 機械学習ガイド : テキスト分類 (2) モデルの選択

TensorFlow 機械学習ガイド : テキスト分類 (2) モデルの選択 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/19/2018

* 本ページは、developers.google.com サイトの Machine Learning Guides : Text classification の以下のページを
翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Step 2.5: モデルを選択する

この時点で、データセットを集めてデータの主要な特性への洞察を得ました。次に、Step 2 で集めたメトリックに基づいて、どの分類モデルを使用するのが良いかを考えるべきです。これは次のような質問を尋ねることを意味します。「数値入力を想定するアルゴリズムに対してどのようにテキストデータを提供するか?」(これはデータ前処理とベクトル化と呼ばれます)、「モデルのどのタイプを使用するべきか?」、「モデルのためにどの構成パラメータを使用するべきか?」、等々。

数十年の研究のおかげで、データ前処理とモデル構成オプションの巨大な配列へのアクセスを持ちます。けれども、そこから選択する実行可能なオプションの非常に巨大な配列の利用可能性は手元の特定の問題の複雑さとスコープを多大に増大させます。最善のオプションが明白でないと仮定すると、素朴な解法は直感を通して幾つかの選択肢を除去しながら、総ての可能なオプションを徹底的に試すことでしょう。けれども、それは非常に高価でしょう。

このガイドでは、テキスト分類モデルを選択する過程を著しく単純化することを試みます。与えられたデータセットに対して、私達の目標は訓練に必要な計算時間を最小化しながら最大精度近くを達成するアルゴリズムを見つけることです。私達は12 データセットを使用し、各データセットに対して異なるデータ前処理技術と異なるモデル・アーキテクチャ間で変更することにより、異なるタイプ (特にセンチメント解析とトピック分類問題) の問題に渡り巨大な数 (~450K) の実験を実行しました。これは最適な選択に影響を与えるデータセット・パラメータを識別するのに役立ちます。

モデル選択アルゴリズムと下のフローチャートは私達の実験の要約です。それらで使用される用語の総てをまだ理解しない場合でも心配しないでください ; このガイドの次のセクションがそれらを深く説明します。

 

データ準備とモデル構築のためのアルゴリズム

  1. number of samples/number of words per sample (= サンプル数 / サンプル毎単語数) の比率を計算する。
  2. この比率が 1500 未満であれば、テキストを n-gram としてトークン化してそれらを分類するために単純な多層パーセプトロン (MLP) モデルを使用する (下のフローチャートの左側の枝) :
    1. サンプルを単語 n-gram に分割する ; n-gram をベクトルに変換する。
    2. ベクトルの重要性を点数づけしてそれからそのスコアを使用して top 20K を選択する。
    3. MLP モデルを構築する。
  3. 比率が 1500 より大きい場合、テキストをシークエンスとしてトークン化してそれらを分類するために sepCNN モデルを使用します (下のフローチャートの右側の枝) :
    1. サンプルを単語に分割する ; それらの頻度に基づいて top 20K 単語を選択する。
    2. サンプルを単語シークエンス・ベクトルに変換する。
    3. 元の number of samples/number of words per sample 比率が 15K 未満である場合、sepCNN モデルとともに再調整されて事前訓練された埋め込みの使用はおそらく最善の結果を提供するでしょう。
  4. データセットのための最善のモデル構成を見つけるために異なるハイパーパラメータでモデル性能を測定する。

下のチャートでは、黄色いボックスはデータとモデル準備の過程を示します。灰色のボックスと緑色のボックスは各過程のために考慮した選択を示します。緑色のボックスは各過程のための推奨される選択を示します。

このフローチャートを貴方の最初の実験を構築する開始点として使用できます、何故ならばそれは低い計算コストで良い精度を与えるからです。それから続く反復に渡り初期モデルを改良し続けることができます。

Figure 5: テキスト分類フローチャート

このフローチャートは 2 つの主要な質問に答えます :

  1. どの学習アルゴリズムかモデルを使用するべきでしょう?
  2. テキストとラベルの関係を効率的に学習するためにどのようにデータを準備するべきでしょう?

2 つ目の質問への答えは最初の質問への答えに依存します ; モデルに供給されるデータを前処理する方法はどのモデルを選択するかに依存します。モデルはおおまかに 2 のカテゴリに分類されます : 単語順序 (= ordering) 情報 (シークエンス・モデル) を使用するものと、テキストを単語の「バッグ」(セット) (= bags of words) (n-gram モデル) として単に見るものです。シークエンスモデルのタイプは畳み込みニューラルネットワーク (CNN)、リカレント・ニューラルネットワーク (RNN)、そしてそれらの変種を含みます。n-gram モデルのタイプはロジスティック回帰、単純な多層パーセプトロン (MLP、あるいは完全結合ニューラルネットワーク)、勾配ブースティング木そしてサポートベクターマシンを含みます。

私達の実験から、“number of samples” (S) の “number of words per sample” (W) への比率はどのモデルが上手く遂行するかに相関関係があることを観察しました

この比率の値が小さい (<1500) とき、入力として n-gram を取る小さい多層パーセプトロン (オプション A と呼びます) はより良いあるいは少なくともシークエンスモデルと同程度に良いです。MLP は定義して理解するために単純で、そしてそれらはシークエンスモデルよりも遥かに少ない計算時間しかかかりません。この比率の値が大きい (>= 1500) とき、シークエンスモデル (オプション B) を使用します。

続くステップでは、 samples/words-per-sample に基づき選択したモデル・タイプのための関連するサブセクション (ラベル付けされた A または B) にスキップすることができます。

IMDb レビュー・データセットの場合では、samples/words-per-sample 比率は ~144 です。これは MLP モデルを作成することを意味します。

Note: 上のフローチャートを使用するとき、幾つかの理由のために、それは貴方の問題に対して最適な結果に必ずしも導かないかもしれないことに留意してください :

  • 貴方の目標は異なるかもしれません。最短の可能な計算時間で達成され得る最善の精度のために最適化しました。代わりのフローはより良い結果を生成するかもしれません、例えば、AUC (area under the curve, 曲線下面積) のために最適化するときです。
  • 典型的で一般的なアルゴリズムを選択しました。分野は発展し続けていますので、新しい最先端のアルゴリズムと拡張が貴方のデータに関係してより良く遂行するかもしれません。
  • フローチャートを導出して検証するために幾つかのデータセットを使用した一方で、他のフローを使用することが好ましい貴方のデータセットに特有の性質があるかもしれません。
 

以上



TensorFlow 機械学習ガイド : テキスト分類 (1) データの収集と調査

TensorFlow 機械学習ガイド : テキスト分類 (1) データの収集と調査 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/18/2018

* 本ページは、developers.google.com サイトの Machine Learning Guides : Text classification の以下のページを
翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

イントロダクション

テキスト分類アルゴリズムは大規模なテキストデータを処理する様々なソフトウェア・システムの心臓部です。電子メール・ソフトウェアは incoming メールが inbox に送られるか spam フォルダにフィルタされるかを決定するためにテキスト分類を使用します。ディスカッション・フォーラムはコメントが不適切とフラグされるべきかを決定するためにテキスト分類を使用します。

これらはトピック分類の 2 つの例で、テキスト文書を事前定義されたトピックのセットの一つに分類します。多くのトピック分類問題では、このカテゴリ分けは主としてテキスト内のキーワードに基づきます。

Figure 1: トピック分類は incoming spam 電子メールに (spam フォルダにフィルタされる) フラグ付けするために使用されます。

テキスト分類の他の一般的なタイプはセンチメント解析で、その目的はテキスト内容の両極性 (= polarity) を識別します : それが表現する意見 (= opinion) のタイプです。これは二値の好き嫌い (= like/dislike) レイティングや 1 から 5 のスター・レイティングのような、より細かい意見のセットの形式を取ることができます。センチメント解析の例としては、人々がブラックパンサー映画を気に入ったか決定するための Twitter 投稿の解析や、またウォルマートのレビューから Nike シューズの新しいブランドの一般人の意見を推定することを含みます。

このガイドはテキスト分類問題を解くための幾つかの主要な機械学習ベストプラクティスを貴方に教えます。これらが学習するものです :

  • 機械学習を使用してテキスト分類問題を解くための高位な、end-to-end ワークフロー
  • テキスト分類問題のために正しいモデルをどのように選択するか
  • 最適なモデルを TensorFlow を使用してどのように実装するか

 

テキスト分類ワークフロー

機械学習問題を解くために使用されるワークフローの高位な概要がここにあります :

  • Step 1: データを収集する
  • Step 2: データを調査する
  • Step 2.5: モデルを選択する
  • Step 3: データを準備する
  • Step 4: モデルを構築、訓練、そして評価する
  • Step 5: ハイパーパラメータを調整する
  • Step 6: モデルを配備する

Figure 2: 機械学習問題を解くためのワークフロー

 

Step 1: データを収集する

どのような教師有り機械学習問題を解く場合でもデータの収集は最も重要なステップです。貴方のテキスト分類器は (それが構築された) データセットと同程度に良いだけです。

もし貴方が解くことを望む特定の問題を持たずに一般的なテキスト分類を調べることに興味があるだけならば、利用可能な多くのオープンソースのデータセットがあります。私達の GitHub repo でそれらの幾つかへのリンクを見つけることができます。その一方で、貴方が特定の問題に取り組んでいるのであれば、必要なデータを集める必要があるでしょう。多くの組織が彼らのデータにアクセスするため公開 API を提供しています — 例えば、Twitter APINY Times API です。貴方が解こうとしている問題に対してこれらを活用できるかもしれません。

データを集めるときに覚えておきたい幾つかの重要なことがここにあります :

  • 公開 API を使用しているならば、それらを使用する前に API の制限を理解しましょう。例えば、幾つかの API は問い合わせ可能なレートについて制限を設定しています。
  • 貴方が持つ訓練例 (このガイドの残りではサンプルとして参照されます) が多ければ多いほど、より良いです。これは貴方のモデルをより良く一般化する助けとなるでしょう。
  • 総てのクラスかトピックに対するサンプルの数は過度に不均衡でないことを確かなものとしてください。つまり、各クラスで同等な数のサンプルを持つべきです。
  • サンプルが一般的なケースだけではなく、起こりうる入力空間を適切にカバーすることを確実にしてください。

このガイドを通して、ワークフローを示すために Internet Movie Database (IMDb) 映画レビュー・データセット を使用します。このデータセットは IMDb web サイト上の人々により投稿された映画レビューと、レビュアーが映画を好んだか否かを示す対応するラベル (ポシティブ or ネガティブ) を含みます。これはセンチメント解析問題の古典的な例です。

 

Step 2: データを調査する

モデルを構築して訓練することはワークフローの一部に過ぎません。貴方のデータの特性を前もって理解することはより良いモデルを構築することを可能にします。これは単純により高い精度を得ることを意味するでしょう。それはまた訓練のためにより少ないデータとより少ない計算リソースを必要とすることも意味します。

 

データセットをロードする

最初に、データセットを Python にロードしましょう。

def load_imdb_sentiment_analysis_dataset(data_path, seed=123):
    """Loads the IMDb movie reviews sentiment analysis dataset.

    # Arguments
        data_path: string, path to the data directory.
        seed: int, seed for randomizer.

    # Returns
        A tuple of training and validation data.
        Number of training samples: 25000
        Number of test samples: 25000
        Number of categories: 2 (0 - negative, 1 - positive)

    # References
        Mass et al., http://www.aclweb.org/anthology/P11-1015

        Download and uncompress archive from:
        http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
    """
    imdb_data_path = os.path.join(data_path, 'aclImdb')

    # Load the training data
    train_texts = []
    train_labels = []
    for category in ['pos', 'neg']:
        train_path = os.path.join(imdb_data_path, 'train', category)
        for fname in sorted(os.listdir(train_path)):
            if fname.endswith('.txt'):
                with open(os.path.join(train_path, fname)) as f:
                    train_texts.append(f.read())
                train_labels.append(0 if category == 'neg' else 1)

    # Load the validation data.
    test_texts = []
    test_labels = []
    for category in ['pos', 'neg']:
        test_path = os.path.join(imdb_data_path, 'test', category)
        for fname in sorted(os.listdir(test_path)):
            if fname.endswith('.txt'):
                with open(os.path.join(test_path, fname)) as f:
                    test_texts.append(f.read())
                test_labels.append(0 if category == 'neg' else 1)

    # Shuffle the training data and labels.
    random.seed(seed)
    random.shuffle(train_texts)
    random.seed(seed)
    random.shuffle(train_labels)

    return ((train_texts, np.array(train_labels)),
            (test_texts, np.array(test_labels)))

 

データをチェックする

データをロードした後、その上で幾つかのチェックを実行することは良い実践です : 2, 3 のサンプルをピックアップしてそれらが貴方の想定と一致しているかどうかを手動で確認します。例えば、センチメント・ラベルがレビューのセンチメントに対応しているかを見るために 2, 3 のランダムなサンプルをプリントします。IMDb データセットからランダムに選んだレビューがここにあります : “Ten minutes worth of story stretched out into the better part of two hours. When nothing of any significance had happened at the halfway point I should have left.” 想定されるセンチメント (ネガティブ) はサンプルのラベルに適合します。

 

主要なメトリックを集める

ひとたびデータを検証したら、貴方のテキスト分類問題を特徴づける助けとなり得る次の重要なメトリックを集めます :

  1. Number of samples: 貴方がデータで持つサンプルの総数。
  2. Number of classes: データのトピックかカテゴリーの総数。
  3. Number of samples per class: クラス (トピック/カテゴリ) 毎のサンプル数。均衡の取れたデータセットでは、総てのクラスはサンプルの同様の数を持つでしょう ; 不均衡なデータセットでは、各クラスのサンプル数は大きく異なるでしょう。
  4. Number of words per sample: 一つのサンプルの単語の Median 数。
  5. Frequency distribution of words: データセットの各単語の頻度 (出現数) を示す分布。
  6. Distribution of sample length: データセットのサンプル毎の単語数を示す分布。

これらのメトリックについてどのような値が IMDb レビュー・データセットのためであるかを見てみましょう (単語頻度とサンプル長分布のプロットのための Figure 3 と 4 を見てください)。

メトリック名 メトリック値
Number of samples 25000
Number of classes 2
Number of samples per class 12500
Number of words per sample 174

Table 1: IMDb レビュー・データセット・メトリック

explore_data.py はこれらのメトリックを計算して解析する関数を含みます。2 つのサンプルがここにあります :

import numpy as np
import matplotlib.pyplot as plt

def get_num_words_per_sample(sample_texts):
    """Returns the median number of words per sample given corpus.

    # Arguments
        sample_texts: list, sample texts.

    # Returns
        int, median number of words per sample.
    """
    num_words = [len(s.split()) for s in sample_texts]
    return np.median(num_words)

def plot_sample_length_distribution(sample_texts):
    """Plots the sample length distribution.

    # Arguments
        samples_texts: list, sample texts.
    """
    plt.hist([len(s) for s in sample_texts], 50)
    plt.xlabel('Length of a sample')
    plt.ylabel('Number of samples')
    plt.title('Sample length distribution')
    plt.show()

Figure 3: Frequency distribution of words for IMDb

Figure 4: Distribution of sample length for IMDb

 

以上



TensorFlow の RNN/LSTM/GRU で消費者感情分析

TensorFlow の LSTM / GRU / bidirectional RNN で IMDb 消費者感情分析

IMDb & LSTM

消費者感情分析という用語が定着しているか分かりませんが、sentiment analysis を意訳したつもりです。IMDb は Internet Movie Database の略です。

LSTM による IMDb を利用した sentiment 分析は元々は Theano のチュートリアルで扱われたものですが :

 

TensorFlow で実装してみました。やることは単純で movie review の評価を positive か negative か二値分類するだけなのですが、LSTM だけでなく GRU と bidirectional RNN でも試してみました。

TensorFlow で実装/訓練

モデルは試行錯誤しましたが、最終的に次のようなモデルを利用しました。
以下は GRU の図ですが LSTM でも同様です :

imdb_model_gru2

それから bidirectional RNN :

imdb_model_bidirectional2

 

訓練は 40 epochs 行ないました。GRU の収束が速いですが LSTM の曲線の方が滑らかですね :

imdb_all_loss3

 

validation 精度は 85 % 前後になりましたが、bidirectional-RNN については 80% 強が上限でした :

imdb_all_acc_validation2

bidirectional-RNN の結果については optimizer やモデルをいじってもあまり変わりませんでしたので、より適合するテーマを見つけた方が良さそうです。

 

以上

AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com