Big Picture Machine Learning:ニューラルネットワークとTensorFlowを使用したテキストの分類

開発者は、機械学習を始めたい場合は、まずアルゴリズムの仕組みを学ぶ必要があるとよく言います。しかし、私の経験はそうでないことを示しています。

まず、全体像、つまりアプリケーションがどのように機能するかを見ることができるはずです。これを理解すると、深く掘り下げて、アルゴリズムの内部動作を調べるのがはるかに簡単になります。

それでは、どのようにして直感を開発し、機械学習の全体像を理解するのでしょうか?これを行う良い方法は、機械学習モデルを作成することです。

これらのすべてのアルゴリズムを最初から作成する方法がまだわからないと仮定すると、これらのすべてのアルゴリズムが既に実装されているライブラリを使用する必要があります。そして、そのライブラリはTensorFlowです。

この記事では、テキストをカテゴリに分類するための機械学習モデルを作成します。次のトピックについて説明します。

  1. TensorFlowの仕組み
  2. 機械学習モデルとは
  3. ニューラルネットワークとは
  4. ニューラルネットワークの学習方法
  5. データを操作してニューラルネットワークの入力に渡す方法
  6. モデルを実行して予測結果を取得する方法

おそらく多くの新しいことを学ぶでしょうから、始めましょう!

TensorFlow

TensorFlowは、Googleが最初に作成した機械学習用のオープンソースライブラリです。ライブラリの名前は、ライブラリの操作方法を理解するのに役立ちます。テンソルは、グラフのノードを流れる多次元配列です。

tf.Graph

TensorFlowのすべての計算は、データフローグラフとして表されます。このグラフには2つの要素があります。

  • 計算の単位を表すtf.Operationのセット
  • データの単位を表すtf.Tensorのセット

これがすべてどのように機能するかを確認するには、このデータフローグラフを作成します。

x + yを計算するグラフ

x = [1,3,6]およびy = [1,1,1]を定義します。グラフはtf.Tensorと連携してデータの単位を表すため、定数テンソルを作成します。

tensorflowをtfとしてインポート
x = tf.constant([1,3,6])
y = tf.constant([1,1,1])

次に、オペレーションユニットを定義します。

tensorflowをtfとしてインポート
x = tf.constant([1,3,6])
y = tf.constant([1,1,1])
op = tf.add(x、y)

すべてのグラフ要素があります。次に、グラフを作成する必要があります。

tensorflowをtfとしてインポート
my_graph = tf.Graph()
my_graph.as_default()の場合:
    x = tf.constant([1,3,6])
    y = tf.constant([1,1,1])
    op = tf.add(x、y)

これがTensorFlowワークフローの仕組みです。最初にグラフを作成してから初めて計算を実行できます(実際に操作でグラフノードを「実行」)。グラフを実行するには、tf.Sessionを作成する必要があります。

tf.Session

tf.Sessionオブジェクトは、Operationオブジェクトが実行される環境をカプセル化し、Tensorオブジェクトが評価されます(ドキュメントから)。そのためには、セッションで使用されるグラフを定義する必要があります。

tensorflowをtfとしてインポート
my_graph = tf.Graph()
tf.Session(graph = my_graph)をセッションとして:
    x = tf.constant([1,3,6])
    y = tf.constant([1,1,1])
    op = tf.add(x、y)

操作を実行するには、メソッドtf.Session.run()を使用します。このメソッドは、必要なグラフフラグメントを実行してすべてのOperationオブジェクトを実行し、引数フェッチで渡されたすべてのTensorを評価することにより、TensorFlow計算の1つの「ステップ」を実行します。あなたの場合、合計操作のステップを実行します:

tensorflowをtfとしてインポート
my_graph = tf.Graph()
tf.Session(graph = my_graph)をセッションとして:
    x = tf.constant([1,3,6])
    y = tf.constant([1,1,1])
    op = tf.add(x、y)
    結果= sess.run(fetches = op)
    印刷(結果)
>>> [2 4 7]

予測モデル

TensorFlowの仕組みがわかったので、予測モデルの作成方法を学習する必要があります。要するに、

機械学習アルゴリズム+データ=予測モデル

モデルを構築するプロセスは次のとおりです。

予測モデルを作成するプロセス

ご覧のとおり、モデルはデータで「トレーニング」された機械学習アルゴリズムで構成されています。モデルを取得すると、次のような結果が得られます。

予測ワークフロー

作成するモデルの目標は、テキストをカテゴリに分類することです。次のように定義します。

入力:テキスト、結果:カテゴリ

すべてのテキストにラベルが付けられたトレーニングデータセットがあります(すべてのテキストには、どのカテゴリに属する​​かを示すラベルがあります)。機械学習では、この種のタスクは教師あり学習と呼ばれます。

「正しい答えを知っています。このアルゴリズムは、トレーニングデータを繰り返し予測し、教師によって修正されます。」—ジェイソンブラウンリー

データをカテゴリに分類するため、分類タスクでもあります。

モデルを作成するには、ニューラルネットワークを使用します。

ニューラルネットワーク

ニューラルネットワークは計算モデルです(数学言語と数学概念を使用してシステムを記述する方法)。これらのシステムは、明示的にプログラムされるのではなく、自己学習およびトレーニングされます。

ニューラルネットワークは、中枢神経系に触発されています。それらには、ニューロンに似たノードが接続されています。

ニューラルネットワーク

パーセプトロンは、最初のニューラルネットワークアルゴリズムでした。この記事では、パーセプトロンの内部の働きを非常によく説明しています(「人工ニューロンの内部」アニメーションは素晴らしいです)。

ニューラルネットワークの仕組みを理解するために、実際にTensorFlowを使用してニューラルネットワークアーキテクチャを構築します。このアーキテクチャは、この例でAymeric Damienによって使用されました。

ニューラルネットワークアーキテクチャ

ニューラルネットワークには2つの隠れ層があります(ネットワークが持つ隠れ層の数を選択する必要がありますが、これはアーキテクチャ設計の一部です)。各非表示レイヤーの役割は、入力を出力レイヤーが使用できるものに変換することです。

非表示レイヤー1

入力層と最初の非表示層

また、1番目の非表示レイヤーに含めるノードの数を定義する必要があります。これらのノードは、フィーチャまたはニューロンとも呼ばれ、上の図では各円で表されています。

入力レイヤーでは、すべてのノードがデータセットの単語に対応します(これがどのように機能するかは後で確認します)。

ここで説明したように、各ノード(ニューロン)には重みが乗算されます。すべてのノードには重み値があり、トレーニングフェーズ中に、ニューラルネットワークは正しい出力を生成するためにこれらの値を調整します(これについては後で説明します)。

各入力ノードに重みを掛けるだけでなく、ネットワークはバイアス(ニューラルネットワークにおけるバイアスの役割)も追加します。

入力に重みを乗算し、値をバイアスに加算した後のアーキテクチャでは、データもアクティベーション関数を通過します。このアクティベーション関数は、各ノードの最終出力を定義します。類推:各ノードがランプであると想像してください。ランプが点灯するかどうかは、アクティベーション関数によってわかります。

アクティベーション関数には多くの種類があります。修正された線形ユニット(ReLu)を使用します。この関数は次のように定義されます。

f(x)= max(0、x)[出力はxまたは0(ゼロ)のいずれか大きい方]

例:ifx = -1の場合、f(x)= 0(zero); x = 0.7の場合、f(x)= 0.7です。

非表示レイヤー2

2番目の隠れ層は、1番目の隠れ層とまったく同じことを行いますが、2番目の隠れ層の入力は1番目の隠れ層の出力になります。

1番目と2番目の隠れ層

出力層

最後に、最後の層である出力層に到達しました。ワンホットエンコーディングを使用して、このレイヤーの結果を取得します。このエンコードでは、1ビットのみの値が1で、他のすべてのビットの値はゼロです。たとえば、3つのカテゴリ(スポーツ、スペース、コンピューターグラフィックス)をエンコードする場合:

+ ------------------- + ----------- +
|カテゴリー|値|
+ ------------------- | ----------- +
|スポーツ| 001 |
|スペース| 010 |
|コンピュータグラフィックス| 100 |
| ------------------- | ----------- |

したがって、出力ノードの数は、入力データセットのクラスの数です。

出力レイヤーの値にも重みが乗算され、バイアスも追加されますが、アクティブ化関数は異なります。

各テキストにカテゴリのラベルを付けたい場合、これらのカテゴリは相互に排他的です(テキストは同時に2つのカテゴリに属しません)。これを考慮するには、ReLuアクティベーション関数を使用する代わりに、Softmax関数を使用します。この関数は、各単位の出力を0〜1の値に変換し、すべての単位の合計が1に等しくなるようにします。これにより、出力は各カテゴリの各テキストの確率を示します。

| 1.2 0.46 |
| 0.9-> [softmax]-> 0.34 |
| 0.4 0.20 |

これで、ニューラルネットワークのデータフローグラフができました。これまで見てきたすべてをコードに変換すると、結果は次のようになります。

#ネットワークパラメータ
n_hidden_​​1 = 10#フィーチャの最初のレイヤー番号
n_hidden_​​2 = 5#フィーチャの2番目のレイヤー番号
n_input = total_words#単語の単語
n_classes = 3#カテゴリ:グラフィックス、スペース、野球
defマルチプルパーセプトロン(input_tensor、重み、バイアス):
    layer_1_multiplication = tf.matmul(input_tensor、weights ['h1'])
    layer_1_addition = tf.add(layer_1_multiplication、biases ['b1'])
    layer_1_activation = tf.nn.relu(layer_1_addition)
#RELUアクティベーションを含む非表示レイヤー
    layer_2_multiplication = tf.matmul(layer_1_activation、weights ['h2'])
    layer_2_addition = tf.add(layer_2_multiplication、biases ['b2'])
    layer_2_activation = tf.nn.relu(layer_2_addition)
#線形アクティベーションの出力レイヤー
    out_layer_multiplication = tf.matmul(layer_2_activation、weights ['out'])
    out_layer_addition = out_layer_multiplication + bias [[out ']
out_layer_additionを返す

(出力層のアクティベーション機能のコードについては後で説明します。)

ニューラルネットワークの学習方法

前に見たように、ネットワークのトレーニング中に重み値が更新されます。 TensorFlow環境でこれがどのように発生するかを見てみましょう。

tf.Variable

重みとバイアスは変数(tf.Variable)に保存されます。これらの変数は、run()の呼び出し間でグラフの状態を維持します。機械学習では、通常、正規分布を介して重みとバイアス値を開始します。

重み= {
    'h1':tf.Variable(tf.random_normal([n_input、n_hidden_​​1]))、
    'h2':tf.Variable(tf.random_normal([n_hidden_​​1、n_hidden_​​2]))、
    'out':tf.Variable(tf.random_normal([n_hidden_​​2、n_classes]))
}
バイアス= {
    'b1':tf.Variable(tf.random_normal([n_hidden_​​1]))、
    'b2':tf.Variable(tf.random_normal([n_hidden_​​2]))、
    'out':tf.Variable(tf.random_normal([n_classes]))
}

ネットワークを初めて実行するとき(つまり、重み値は正規分布によって定義された値です):

入力値:x
重み:w
バイアス:b
出力値:z
期待値:期待

ネットワークが学習しているかどうかを知るには、出力値(z)を期待値(予想)と比較する必要があります。そして、この差(損失)をどのように計算しますか?それを行うには多くの方法があります。分類タスクを使用しているため、損失の最良の尺度はクロスエントロピー誤差です。

James D. McCaffreyは、これがこの種のタスクに最適な方法である理由について素晴らしい説明を書きました。

TensorFlowでは、tf.nn.softmax_cross_entropy_with_logits()メソッド(ここではsoftmaxアクティベーション関数)を使用してクロスエントロピーエラーを計算し、平均エラー(tf.reduce_mean())を計算します。

#モデルの構築
予測=マルチレイヤーパーセプトロン(入力テンソル、重み、バイアス)
#損失の定義
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits = prediction、labels = output_tensor)
損失= tf.reduce_mean(entropy_loss)

出力エラー(取得した値と正しい値の差)を最小限に抑えるために、重みとバイアスの最適な値を見つけたいと思います。これを行うには、勾配降下法を使用します。具体的には、確率的勾配降下法を使用します。

勾配降下。ソース:https://sebastianraschka.com/faq/docs/closed-form-vs-gd.html

勾配降下を計算するアルゴリズムも多数あります。適応モーメント推定(Adam)を使用します。 TensorFlowでこのアルゴリズムを使用するには、learning_rate値を渡す必要があります。これにより、値の増分ステップが決定され、最適な重み値が見つかります。

メソッドtf.train.AdamOptimizer(learning_rate).minimize(loss)は、2つのことを行う構文糖衣です。

  1. compute_gradients(loss、<変数のリスト>)
  2. apply_gradients(<変数のリスト>)

このメソッドはすべてのtf.Variablesを新しい値で更新するため、変数のリストを渡す必要はありません。そして、これでネットワークをトレーニングするコードができました。

learning_rate = 0.001
#モデルの構築
予測=マルチレイヤーパーセプトロン(入力テンソル、重み、バイアス)
#損失の定義
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits = prediction、labels = output_tensor)
損失= tf.reduce_mean(entropy_loss)
オプティマイザー= tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(loss)

データ操作

使用するデータセットには英語のテキストが多数含まれているため、このデータを操作してニューラルネットワークに渡す必要があります。そのためには、次の2つのことを行います。

  1. 単語ごとにインデックスを作成する
  2. 各テキストのマトリックスを作成します。テキストに単語が含まれている場合は値は1、そうでない場合は0です。

このプロセスを理解するためのコードを見てみましょう。

numpyをnpとしてインポート#numpyは科学計算用のパッケージ
コレクションのインポートカウンターから
vocab = Counter()
テキスト=「ブラジルからこんにちは」
#すべての単語を取得
text.split( '')内の単語の場合:
    vocab [word] + = 1
        
#単語をインデックスに変換する
def get_word_2_index(vocab):
    word2index = {}
    for i、word in enumerate(vocab):
        word2index [word] = i
        
    word2indexを返す
#これでインデックスができました
word2index = get_word_2_index(vocab)
total_words = len(vocab)
#これは、numpy配列(マトリックス)を作成する方法です
マトリックス= np.zeros((total_words)、dtype = float)
#値を入力します
text.split()内の単語の場合:
    matrix [word2index [word]] + = 1
印刷(マトリックス)
>>> [1. 1. 1.]

上記の例では、テキストは「Hi from Brazil」で、マトリックスは[1. 1. 1.]でした。テキストが「こんにちは」だけの場合はどうなりますか?

マトリックス= np.zeros((total_words)、dtype = float)
テキスト=「こんにちは」
text.split()内の単語の場合:
    matrix [word2index [word.lower()]] + = 1
印刷(マトリックス)
>>> [1. 0. 0.]

ラベル(テキストのカテゴリ)でも同じようになりますが、ここではワンホットエンコードを使用します。

y = np.zeros((3)、dtype = float)
カテゴリ== 0の場合:
    y [0] = 1.#[1. 0. 0.]
elifカテゴリ== 1:
    y [1] = 1.#[0. 1. 0.]
その他:
     y [2] =1。#[0. 0. 1.]

グラフを実行して結果を取得する

ここで最も重要なのは、モデルから結果を取得することです。まず、入力データセットを詳しく見てみましょう。

データセット

20のトピックに関する18.000の投稿を含むデータセットである20のニュースグループを使用します。このデータセットを読み込むには、scikit-learnライブラリを使用します。 comp.graphics、sci.space、rec.sport.baseballの3つのカテゴリのみを使用します。 scikit-learnには、トレーニング用とテスト用の2つのサブセットがあります。モデルの作成中に選択を妨げる可能性があるため、テストデータを確認しないことをお勧めします。この特定のテストデータを予測するモデルを作成するのではなく、適切に一般化したモデルを作成します。

これは、データセットをロードする方法です。

sklearn.datasetsからimport fetch_20newsgroups
カテゴリ= ["comp.graphics"、 "sci.space"、 "rec.sport.baseball"]
newsgroups_train = fetch_20newsgroups(subset = 'train'、Categories = categories)
newsgroups_test = fetch_20newsgroups(subset = 'test'、Categories = categories)

モデルのトレーニング

ニューラルネットワークの用語では、1つのエポック=すべてのトレーニング例の1つのフォワードパス(出力値の取得)と1つのバックワードパス(重みの更新)です。

tf.Session.run()メソッドを覚えていますか?それを詳しく見てみましょう:

tf.Session.run(fetches、feed_dict = None、options = None、run_metadata = None)

この記事の最初のデータフローグラフでは、合計演算を使用しましたが、実行するもののリストを渡すこともできます。このニューラルネットワークの実行では、損失の計算と最適化の2つのステップを通過します。

feed_dictパラメーターは、各実行ステップのデータを渡す場所です。このデータを渡すには、tf.placeholdersを定義する必要があります(feed_dictにフィードするため)。

TensorFlowのドキュメントにあるように:

「プレースホルダーは、フィードのターゲットとしてのみ機能します。初期化されておらず、データも含まれていません。」—ソース

したがって、次のようにプレースホルダーを定義します。

n_input = total_words#単語の単語
n_classes = 3#カテゴリ:グラフィックス、sci.spaceおよび野球
input_tensor = tf.placeholder(tf.float32、[None、n_input]、name = "input")
output_tensor = tf.placeholder(tf.float32、[None、n_classes]、name = "output")

トレーニングデータをバッチに分けます。

「入力のフィードにプレースホルダーを使用する場合、tf.placeholder(…、shape = [None、…])を使用してプレースホルダーを作成することにより、可変バッチディメンションを指定できます。形状のNone要素は、可変サイズのディメンションに対応しています。」—ソース

モデルのテスト中に辞書に大きなバッチを入力します。そのため、可変バッチディメンションを定義する必要があります。

get_batches()関数は、バッチのサイズを含むテキストの数を提供します。これで、モデルを実行できます。

training_epochs = 10
#グラフを起動
tf.Session()をセッションとして:
    sess.run(init)#変数を初期化します(正規分布、覚えていますか?)
    #トレーニングサイクル
    範囲内のエポック(training_epochs)の場合:
        avg_cost = 0。
        total_batch = int(len(newsgroups_train.data)/ batch_size)
        #すべてのバッチをループする
        範囲内のi(total_batch):
            batch_x、batch_y = get_batch(newsgroups_train、i、batch_size)
            #最適化op(backprop)およびcost op(損失値を取得する)を実行します
            c、_ = sess.run([loss、optimizer]、feed_dict = {input_tensor:batch_x、output_tensor:batch_y})

これで、訓練されたモデルができました。テストするには、グラフ要素も作成する必要があります。モデルの精度を測定するので、予測値のインデックスと正しい値のインデックスを取得する必要があります(ワンホットエンコーディングを使用しているため)、それらが等しいかどうかを確認し、平均を計算しますすべてのテストデータセット:

    #テストモデル
    index_prediction = tf.argmax(prediction、1)
    index_correct = tf.argmax(output_tensor、1)
    correct_prediction = tf.equal(index_prediction、index_correct)
    #精度を計算する
    精度= tf.reduce_mean(tf.cast(correct_prediction、 "float"))
    total_test_data = len(newsgroups_test.target)
    batch_x_test、batch_y_test = get_batch(newsgroups_test、0、total_test_data)
    print( "Accuracy:"、precision.eval({input_tensor:batch_x_test、output_tensor:batch_y_test}))
>>>エポック:0001損失= 1133.908114347
    エポック:0002損失= 329.093700409
    エポック:0003損失= 111.876660109
    エポック:0004損失= 72.552971845
    エポック:0005損失= 16.673050320
    エポック:0006損失= 16.481995190
    エポック:0007損失= 4.848220565
    エポック:0008損失= 0.759822878
    エポック:0009損失= 0.000000000
    エポック:0010損失= 0.079848485
    最適化が完了しました!
    精度:0.75

以上です!ニューラルネットワークを使用してテキストをカテゴリに分類するモデルを作成しました。おめでとうございます!

最終コードが記載されたノートブックをここで見ることができます。

ヒント:定義した値を変更して、変更がトレーニング時間とモデルの精度にどのように影響するかを確認します。

質問や提案はありますか?コメントに残してください。ああ、読んでくれてありがとう!

この記事は役に立ちましたか?毎月、詳細な記事を書くように最善を尽くしています。新しい記事を公開するときにメールを受け取ることができます。

をクリックして友達と共有すると、とても意味があります。データサイエンスと機械学習の詳細については、こちらをご覧ください。