開發人員經常說,如果你想開始機器學習,你應該首先學習演算法。但是我的經驗則不是。
我說你應該首先了解:應用程式如何工作。一旦瞭解了這一點,深入探索演算法的內部工作就會變得更加容易。
那麼,你如何 開發直覺學習,並實現理解機器學習這個目的?一個很好的方法是建立機器學習模型。
假設您仍然不知道如何從頭開始建立所有這些演算法,您可以使用一個已經為您實現所有這些演算法的庫。那個庫是 TensorFlow。
在本文中,我們將建立一個機器學習模型來將文字分類到類別中。我們將介紹以下主題:
- TensorFlow 的工作原理
- 什麼是機器學習模型
- 什麼是神經網路
- 神經網路如何學習
- 如何運算元據並將其傳遞給神經網路
- 如何執行模型並獲得預測結果
你可能會學到很多新東西,所以讓我們開始吧!
TensorFlow
TensorFlow 是一個機器學習的開源庫,由 Google 首創。庫的名稱幫助我們理解我們怎樣使用它:tensors 是通過圖的節點流轉的多維陣列。
tf.Graph
在 TensorFlow 中的每一個計算都表示為資料流圖,這個圖有兩類元素:
- 一類 tf.Operation,表示計算單元
- 一類 tf.Tensor,表示資料單元
要檢視這些是怎麼工作的,你需要建立這個資料流圖:
(計算x+y的圖)
你需要定義 x = [1,3,6] 和 y = [1,1,1]。由於圖用 tf.Tensor 表示資料單元,你需要建立常量 Tensors:
1 2 3 |
import tensorflow as tf x = tf.constant([1,3,6]) y = tf.constant([1,1,1]) |
現在你將定義操作單元:
1 2 3 4 |
import tensorflow as tf x = tf.constant([1,3,6]) y = tf.constant([1,1,1]) op = tf.add(x,y) |
你有了所有的圖元素。現在你需要構建圖:
1 2 3 4 5 6 |
import tensorflow as tf my_graph = tf.Graph() with 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 物件是被計算過的(從文件中)。為了做到這些,我們需要在 Session 中定義哪個圖將被使用到:
1 2 3 4 5 6 |
import tensorflow as tf my_graph = tf.Graph() with tf.Session(graph=my_graph) as sess: x = tf.constant([1,3,6]) y = tf.constant([1,1,1]) op = tf.add(x,y) |
為了執行操作,你需要使用方法 tf.Session.run()。這個方法通過執行必要的圖段去執行每個 Operation 物件並通過引數 fetches 計算每一個 Tensor 的值的方式執行 TensorFlow 計算的一’步’:
1 2 3 4 5 6 7 8 9 |
import tensorflow as tf my_graph = tf.Graph() with tf.Session(graph=my_graph) as sess: x = tf.constant([1,3,6]) y = tf.constant([1,1,1]) op = tf.add(x,y) result = sess.run(fetches=op) print(result) >>> [2 4 7] |
預測模型
現在你知道了 TensorFlow 的工作原理,那麼你得知道怎樣建立預測模型。簡而言之
機器學習演算法+資料=預測模型
構建模型的過程就是這樣:
(構建預測模型的過程)
正如你能看到的,模型由資料“訓練過的”機器學習演算法組成。當你有了模型,你就會得到這樣的結果:
(預測工作流)
你建立的模型的目的是對文字分類,我們定義了:
input: text, result: category
我們有一個使用已經標記過的文字(每個文字都有了它屬於哪個分類的標記)訓練的資料集。在機器學習中,這種任務的型別是被稱為監督學習。
“我們知道正確的答案。該演算法迭代的預測訓練資料,並由老師糾正
” — Jason Brownlee
你會把資料分成類,因此它也是一個分類任務。
為了建立這個模型,我們將會用到神經網路。
神經網路
神經網路是一個計算模型(一種描述使用機器語言和數學概念的系統的方式)。這些系統是自主學習和被訓練的,而不是明確程式設計的。
神經網路是也從我們的中樞神經系統受到的啟發。他們有與我們神經相似的連線節點。
(一個神經網路)
感知器是第一個神經網路演算法。這篇文章 很好地解釋了感知器的內部工作原理(“人工神經元內部” 的動畫非常棒)。
為了理解神經網路的工作原理,我們將會使用 TensorFlow 建立一個神經網路架構。在這個例子中,這個架構被 Aymeric Damien 使用過。
神經網路架構
神經網路有兩個隱藏層(你得選擇 網路會有多少隱藏層,這是結構設計的一部分)。每一個隱藏層的任務是 把輸入的東西轉換成輸出層可以使用的東西。
隱藏層 1
(輸入層和第一個隱藏層)
你也需要定義第一個隱藏層會有多少節點。這些節點也被稱為特徵或神經元,在上面的例子中我們用每一個圓圈表示一個節點。
輸入層的每個節點都對應著資料集中的一個詞(之後我們會看到這是怎麼執行的)
如 這裡 所述,每個節點(神經元)乘以一個權重。每個節點都有一個權重值,在訓練階段,神經網路會調整這些值以產生正確的輸出(過會,我們將會學習更多關於這個的資訊)
除了乘以沒有輸入的權重,網路也會增加一個誤差 (在神經網路中誤差的角色)。
在你的架構中,將輸入乘以權重並將值與偏差相加,這些資料也要通過啟用函式傳遞。這個啟用函式定義了每個節點的最終輸出。比如說:想象一下,每一個節點是一盞燈,啟用函式決定燈是否會亮。
有很多型別的啟用函式。你將會使用 Rectified Linear Unit (ReLu)。這個函式是這樣定義的:
f(x) = max(0,x) [輸出 x 或者 0(零)中最大的數]
例如:如果 x = -1, f(x) = 0(zero); 如果 x = 0.7, f(x) = 0.7.
隱藏層 2
第二個隱藏層做的完全是第一個隱藏層做的事情,但現在第二層的輸入是第一層的輸出。
(第一和第二隱藏層)
輸出層
現在終於到了最後一層,輸出層。你將會使用 One-Hot 編碼 得到這個層的結果。在這個編碼中,只有一個位元的值是 1,其他位元的值都是 0。例如,如果我們想對三個分類編碼(sports, space 和computer graphics)編碼:
1 2 3 4 5 6 7 |
+-------------------+-----------+ | category | value | +-------------------|-----------+ | sports | 001 | | space | 010 | | computer graphics | 100 | |-------------------|-----------| |
因此輸出節點的編號是輸入的資料集的分類的編號。
輸出層的值也要乘以權重,並我們也要加上誤差,但是現在啟用函式不一樣。
你想用分類對每一個文字進行標記,並且這些分類相互獨立(一個文字不能同時屬於兩個分類)。考慮到這點,你將使用 Softmax 函式而不是 ReLu 啟用函式。這個函式把每一個完整的輸出轉換成 0 和 1 之間的值,並且確保所有單元的和等於一。這樣,輸出將告訴我們每個分類中每個文字的概率。
1 2 3 |
| 1.2 0.46| | 0.9 -> [softmax] -> 0.34| | 0.4 0.20| |
現在有了神經網路的資料流圖。把我們所看到的都轉換為程式碼,結果是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Network Parameters n_hidden_1 = 10 # 1st layer number of features n_hidden_2 = 5 # 2nd layer number of features n_input = total_words # Words in vocab n_classes = 3 # Categories: graphics, space and baseball def multilayer_perceptron(input_tensor, weights, biases): 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) # Hidden layer with RELU activation 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) # Output layer with linear activation out_layer_multiplication = tf.matmul(layer_2_activation, weights['out']) out_layer_addition = out_layer_multiplication + biases['out']return out_layer_addition |
(我們將會在後面討論輸出層的啟用函式)
神經網路怎麼學習
就像我們前面看到的那樣,神經網路訓練時會更新權重值。現在我們將看到在 TensorFlow 環境下這是怎麼發生的。
tf.Variable
權重和誤差儲存在變數(tf.Variable)中。這些變數通過呼叫 run() 保持在圖中的狀態。在機器學習中我們一般通過 正太分佈 來啟動權重和偏差值。
1 2 3 4 5 6 7 8 9 10 |
weights = { '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])) } biases = { '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])) } |
當我們第一次執行神經網路的時候(也就是說,權重值是由正態分佈定義的):
1 2 3 4 5 |
input values: x weights: w bias: b output values: z expected values: expected |
為了知道網路是否正在學習,你需要比較一下輸出值(Z)和期望值(expected)。我們要怎麼計算這個的不同(損耗)呢?有很多方法去解決這個問題。因為我們正在進行分類任務,測量損耗的最好的方式是 交叉熵誤差。
James D. McCaffrey 寫了一個精彩的解釋,說明為什麼這是這種型別任務的最佳方法。
通過 TensorFlow 你將使用 tf.nn.softmax_cross_entropy_with_logits() 方法計算交叉熵誤差(這個是 softmax 啟用函式)並計算平均誤差 (tf.reduced_mean())。
1 2 3 4 5 |
# Construct model prediction = multilayer_perceptron(input_tensor, weights, biases) # Define loss entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor) loss = tf.reduce_mean(entropy_loss) |
你希望通過權重和誤差的最佳值,以便最小化輸出誤差(實際得到的值和正確的值之間的區別)。要做到這一點,將需使用 梯度下降法。更具體些是,需要使用 隨機梯度下降。
(梯度下降。源: https://sebastianraschka.com/faq/docs/closed-form-vs-gd.html)
為了計算梯度下降,將要使用 Adaptive Moment Estimation (Adam)。要在 TensorFlow 中使用此演算法,需要傳遞 learning_rate 值,該值可確定值的增量步長以找到最佳權重值。
方法 tf.train.AdamOptimizer(learning_rate).minimize(loss) 是一個 語法糖,它做了兩件事情:
- compute_gradients(loss, <list of variables>)
- apply_gradients(<list of variables>)
這個方法用新的值更新了所有的 tf.Variables ,因此我們不需要傳遞變數列表。現在你有了訓練網路的程式碼:
1 2 3 4 5 6 7 |
learning_rate = 0.001 # Construct model prediction = multilayer_perceptron(input_tensor, weights, biases) # Define loss entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor) loss = tf.reduce_mean(entropy_loss) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss) |
資料操作
將要使用的資料集有很多英文文字,我們需要操作這些資料將其傳遞給神經網路。要做到這一點,需要做兩件事:
- 為每一個工作建立索引
- 為每一個文字建立矩陣,在矩陣裡,如果單詞在文字中則值為 1,否則值為 0
讓我們看著程式碼來理解這個過程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import numpy as np #numpy is a package for scientific computing from collections import Counter vocab = Counter() text = "Hi from Brazil"#Get all wordsfor word in text.split(' '): vocab[word]+=1 #Convert words to indexes def get_word_2_index(vocab): word2index = {} for i,word in enumerate(vocab): word2index[word] = i return word2index #Now we have an index word2index = get_word_2_index(vocab) total_words = len(vocab) #This is how we create a numpy array (our matrix) matrix = np.zeros((total_words),dtype=float) #Now we fill the valuesfor word in text.split(): matrix[word2index[word]] += 1print(matrix) >>> [ 1. 1. 1.] |
上面例子中的文字是‘Hi from Brazil’,矩陣是 [ 1. 1. 1.]。如果文字僅是‘Hi’會怎麼樣?
1 2 3 4 |
matrix = np.zeros((total_words),dtype=float) text = "Hi"for word in text.split(): matrix[word2index[word.lower()]] += 1print(matrix) >>> [ 1. 0. 0.] |
將會與標籤(文字的分類)相同,但是現在得使用獨熱編碼(one-hot encoding):
1 2 3 4 5 |
y = np.zeros((3),dtype=float)if category == 0: y[0] = 1. # [ 1. 0. 0.] elif category == 1: y[1] = 1. # [ 0. 1. 0.]else: y[2] = 1. # [ 0. 0. 1.] |
執行圖並獲取結果
現在進入最精彩的部分:從模型中獲取結果。先仔細看看輸入的資料集。
資料集
對於一個有 18.000 個帖子大約有 20 個主題的資料集,將會使用到 20個新聞組。要載入這些資料集將會用到 scikit-learn 庫。我們只使用 3 種類別:comp.graphics, sci.space 和 rec.sport.baseball。scikit-learn 有兩個子集:一個用於訓練,另一個用於測試。建議不要檢視測試資料,因為這可能會在建立模型時干擾你的選擇。你不會希望建立一個模型來預測這個特定的測試資料,因為你希望建立一個具有很好的泛化效能的模型。
這裡是如何載入資料集的程式碼:
1 2 3 4 |
from sklearn.datasets import fetch_20newsgroups categories = ["comp.graphics","sci.space","rec.sport.baseball"] newsgroups_train = fetch_20newsgroups(subset='train', categories=categories) newsgroups_test = fetch_20newsgroups(subset='test', categories=categories) |
訓練模型
在 神經網路的術語裡,一次 epoch = 一個向前傳遞(得到輸出的值)和一個所有訓練示例的向後傳遞(更新權重)。
還記得 tf.Session.run() 方法嗎?讓我們仔細看看它:
tf.Session.run(fetches, feed_dict=None, options=None, run_metadata=None)
在這篇文章開始的資料流圖裡,你用到了和操作,但是我們也可以傳遞一個事情的列表用於執行。在這個神經網路執行中將傳遞兩個事情:損耗計算和優化步驟。
feed_dict 引數是我們為每步執行所輸入的資料。為了傳遞這個資料,我們需要定義tf.placeholders(提供給 feed_dict)
正如 TensorFlow 文件中說的:
“佔位符的存在只作為輸入的目標,它不需要初始化,也不包含資料。” — Source
因此將要像這樣定義佔位符:
1 2 3 4 |
n_input = total_words # Words in vocab n_classes = 3 # Categories: graphics, sci.space and baseball 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, …]) 建立佔位符來指定變數批量維度。shape 的 None 元素對應於大小可變的維度。” — Source
在測試模型時,我們將用更大的批處理來提供字典,這就是為什麼需要定義一個可變的批處理維度。
get_batches() 函式為我們提供了批處理大小的文字數。現在我們可以執行模型:
1 2 3 4 5 6 7 8 9 10 |
training_epochs = 10# Launch the graph with tf.Session() as sess: sess.run(init) #inits the variables (normal distribution, remember?) # Training cycle for epoch in range(training_epochs): avg_cost = 0. total_batch = int(len(newsgroups_train.data)/batch_size) # Loop over all batches for i in range(total_batch): batch_x,batch_y = get_batch(newsgroups_train,i,batch_size) # Run optimization op (backprop) and cost op (to get loss value) c,_ = sess.run([loss,optimizer], feed_dict={input_tensor: batch_x, output_tensor:batch_y}) |
現在有了這個經過訓練的模型。為了測試它,還需要建立圖元素。我們將測量模型的準確性,因此需要獲取預測值的索引和正確值的索引(因為我們使用的是獨熱編碼),檢查它們是否相等,並計算所有測試資料集的平均值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Test model index_prediction = tf.argmax(prediction, 1) index_correct = tf.argmax(output_tensor, 1) correct_prediction = tf.equal(index_prediction, index_correct) # Calculate accuracy accuracy = 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:", accuracy.eval({input_tensor: batch_x_test, output_tensor: batch_y_test})) >>> Epoch: 0001 loss= 1133.908114347 Epoch: 0002 loss= 329.093700409 Epoch: 0003 loss= 111.876660109 Epoch: 0004 loss= 72.552971845 Epoch: 0005 loss= 16.673050320 Epoch: 0006 loss= 16.481995190 Epoch: 0007 loss= 4.848220565 Epoch: 0008 loss= 0.759822878 Epoch: 0009 loss= 0.000000000 Epoch: 0010 loss= 0.079848485 Optimization Finished! Accuracy: 0.75 |
就是這樣!你使用神經網路建立了一個模型來將文字分類到不同的類別中。恭喜!
可在 這裡 看到包含最終程式碼的筆記本。
提示:修改我們定義的值,以檢視更改如何影響訓練時間和模型精度。
還有其他問題或建議?留下你們的評論。謝謝閱讀!