【實戰(zhàn)】使用神經(jīng)網(wǎng)絡(luò)和TensorFlow來對文本分類

1.機器學(xué)習(xí)算法 + 數(shù)據(jù) = 預(yù)測模型

由于我們定義的模型是用來對文本進行分類,所以定義如下:
輸入: 文本,
輸出: 分類

接著,我們需要一個做了標(biāo)記的文本訓(xùn)練集(每個文本對應(yīng)一個分類標(biāo)志)。在機器學(xué)習(xí)里,這種情況叫做監(jiān)督學(xué)習(xí)(Supervised learning)。

由于是把數(shù)據(jù)分類,所以這個又是分類任務(wù)。為了創(chuàng)建這個模型,我們將使用神經(jīng)網(wǎng)絡(luò)。

2.神經(jīng)網(wǎng)絡(luò)

神經(jīng)網(wǎng)絡(luò)的靈感來自我們的中樞神經(jīng)系統(tǒng),相互連接的節(jié)點就像我們的神經(jīng)單元一樣:

image

2.1神經(jīng)網(wǎng)絡(luò)架構(gòu)

本神經(jīng)網(wǎng)絡(luò)由兩個隱藏層組成(至于選擇多少層隱藏層,這是屬于神經(jīng)網(wǎng)絡(luò)的架構(gòu)設(shè)計)。每個隱藏層的工作是將輸入轉(zhuǎn)換成輸出層可以使用的東西。

  • 第一層隱藏層
    你需要定義第一層隱藏層有多少個節(jié)點,這些節(jié)點也叫做特征值或者神經(jīng)元,在上圖里表示為圓圈。

每個節(jié)點(神經(jīng)元)都乘以一個權(quán)重值。每個節(jié)點有一個權(quán)重值,在訓(xùn)練期間,神經(jīng)網(wǎng)絡(luò)通過調(diào)整這些權(quán)重值,以便可以生產(chǎn)一個正確的輸出。
除了每個輸入節(jié)點乘以權(quán)重值之外,神經(jīng)網(wǎng)絡(luò)還需要加上一個偏差值

在我們的構(gòu)架里輸入的值乘以權(quán)重值,加上偏差值,然后通過激活函數(shù)。激活函數(shù)是定義在每個輸出節(jié)點的后面,可以這樣來理解:假設(shè)每個節(jié)點都是燈,激活函數(shù)告訴燈是否亮。

激活函數(shù)有很多種類型,最常使用的非線性激活函數(shù)(ReLu),它定義如下:
圖片:
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1516426898949&di=67340e2c677b2c6a855891f47907987c&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20161013%2F417f598b58914ed6a46a714c05b79f6f.png

  • 第二層隱藏層

其實第二層隱藏層的工作方式與第一層是一樣的,只不過,第二層是從第一層進行輸入的:


image
  • 輸出層
    需要使用one-hot-encoding(獨熱編碼
    )來表示輸出結(jié)果,僅有一個元素為1,其它元素為0.假如,我們使用三個分類(體育、太空和計算機圖形學(xué)):

由上可知,輸出節(jié)點的數(shù)目就是數(shù)據(jù)集分類的數(shù)目。

+-------------------+-----------+
| category | value |
+-------------------|-----------+
| sports | 001 |
| space | 010 |
| computer graphics | 100 |

輸出層的數(shù)值也是乘以權(quán)重值,并加上偏差值,但最后的激活函數(shù)是不一樣的。

您希望將每個文本標(biāo)記為一個類別,這些類別是相互排斥的(意味著每個文本不可能同時屬于兩種類別)。考慮到這些,顯然使用非線性激活函數(shù)ReLu就不行了,因而采用Softmax函數(shù),這個函數(shù)轉(zhuǎn)換輸出結(jié)果為0和1之間,且所有元素相加起來等于1,通過這樣的方式告訴我們每個分類的文本的概率:

Softmax函數(shù)

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

到這里已經(jīng)把全部神經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)流圖說完了,可根據(jù)整個過程轉(zhuǎn)換為代碼,如下:

# -*- coding: utf-8 -*-
"""
Created on Sat Jan 20 11:03:16 2018

@author: JayMo
"""

# 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

2.2神經(jīng)網(wǎng)絡(luò)怎么樣學(xué)習(xí)

從上面我們看到,權(quán)重值是在神經(jīng)網(wǎng)絡(luò)訓(xùn)練的過程中更新的,下面通過TensorFlow的環(huán)境里來查看神經(jīng)網(wǎng)絡(luò)是怎么樣學(xué)習(xí)的。

tf.Variable
權(quán)重值和偏差值都是保存在變量(tf.Variable)里,通過調(diào)用函數(shù)run()來維護和更新這些變量的狀態(tài)。在機器學(xué)習(xí)的初始階段,我們常常把這些權(quán)重值和偏差值初始化為正態(tài)分布的值

weights = {

    'hidden1': 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 = {

    'biases1': 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]))

}

當(dāng)我們第一次運行神經(jīng)網(wǎng)絡(luò)時,這些權(quán)重值和偏差值都采用正態(tài)分布的值來初始化:

  • input values: x
  • weights: w
  • bias: b
  • output values: z
  • expected values: expected

神經(jīng)網(wǎng)絡(luò)為了知道怎么樣學(xué)習(xí),需要比較輸出值(z)和期望值(expected)之間的差異,然后通過計算它們之間的差(損失)?計算這種之間的差別有很多方式,但我們這里的任務(wù)是分類任務(wù),因此最好的損失函數(shù)是采用交叉熵的方式

在TensorFlow里用來計算交叉熵的函數(shù)是tf.nn.softmax_cross_entroy_with_logits(),接著使用平均誤差tf.reduced_mean()來計算:

# 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)

為了讓輸出的誤差最小化(輸出值與期望值之間的差最?。托枰业胶线m的權(quán)重值和偏差值。要完成這個任務(wù),因而就引入了梯度下降的算法,更加直接一點就是采用隨機梯度下降算法


image

同樣也有很多算法來計算梯度下降的,在這里采用Adaptive Moment Estimation (Adam,自適應(yīng)矩估計)算法計算。在TensorFlow里使用這個算法時,需要輸入一個學(xué)習(xí)速率的參數(shù),這個參數(shù)決定了每一次找到最好的權(quán)重值的步伐。

方法tf.train.AdamOptimizer(learning_rate).minimize(loss)是分成兩步計算的,如下:

  1. 計算梯度(損失值, <變量列表>)
  2. 更新梯度(<變量列表>)

這個方法更新了所有變量tf.Variables為新值,所以不需傳送變量列表??梢园延?xùn)練的代碼像下面這樣編寫:

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) 

2.3數(shù)據(jù)操作

在這個數(shù)據(jù)集里使用英語文本來表示,需要把這些數(shù)據(jù)通過神經(jīng)網(wǎng)絡(luò),下面需要做兩件事情:

  1. 每個單詞創(chuàng)建一個索引
  2. 每句話創(chuàng)建一個矩陣,如果單詞出現(xiàn)標(biāo)記為1,否則標(biāo)記為0.
import numpy as np    #numpy is a package for scientific computing  
from collections import Counter  
vocab = Counter()  
text = "Hi from Brazil"  
#Get all words  
for 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 values  
for word in text.split():  
    matrix[word2index[word]] += 1  
print(matrix)  
>>> [ 1.  1.  1.]  
  
在這個例子里,句子“Hi from Brazil”變成矩陣[1. 1. 1.]表示,如果句子只有單詞“Hi”時怎么樣表示?  
matrix = np.zeros((total_words),dtype=float)  
text = "Hi"  
for word in text.split():  
    matrix[word2index[word.lower()]] += 1  
print(matrix)  
>>> [ 1.  0.  0.] 

對于標(biāo)簽也可采用同樣方法,不過是采用one-hot向量的方式:

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.]

2.4運行數(shù)據(jù)流圖和獲取計算結(jié)果

  • 數(shù)據(jù)集
    可以使用20個新聞組(http://qwone.com/~jason/20Newsgroups/)的數(shù)據(jù),20個主題組成,大概有18000個篇文章。要加載這些數(shù)據(jù)需要使用scikit-learn庫,在這里僅使用三個分類:comp.graphics, sci.space 和rec.sport.baseball。Scikit-learn有兩個集合組成:一個訓(xùn)練集和測試集。建議你不要看測試數(shù)據(jù),因為在創(chuàng)建模型時會干擾你的選擇。你不想創(chuàng)建一個模型來預(yù)測這個特定的測試數(shù)據(jù),你想創(chuàng)建一個模型,具有良好的泛化。

下面就是加載數(shù)據(jù)的代碼:

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)
  • 訓(xùn)練模型

在神經(jīng)網(wǎng)絡(luò)的術(shù)語里,一個周期等于一遍數(shù)據(jù)通過(獲取輸出值)和一遍反饋(更新權(quán)重值)。

還記得方法tf.Session.run()嗎?讓我們再來仔細看一下:
tf.Session.run(fetches, feed_dict=None, options=None, run_metadata=None)

在剛開始的神經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)流圖,我們使用相加的操作,但現(xiàn)在我們能傳送一系列的操作了。在神經(jīng)網(wǎng)絡(luò)里,你主要做兩件事情:損失值計算和每一步優(yōu)化。

參數(shù)feed_dict是每一步運行時的傳送數(shù)據(jù)的參數(shù),為了傳送數(shù)據(jù),需要使用類(tf.placeholders)定義變量。

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")

訓(xùn)練時可以使用分批進行:

我們將字典具有較大的批量測試時的模型,這就是為什么你需要定義一個變量的批處理尺寸。

因此定義函數(shù)get_batches()來獲取批量:

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})

到這里已經(jīng)有一個被訓(xùn)練的模型了,為了測試它,必須創(chuàng)建一個測試運行圖。我們將測量模型的準(zhǔn)確性,所以你需要得到預(yù)測值的指數(shù)和正確值的索引(因為我們使用的是一個熱編碼),檢查它們是否相等,并計算所有測試數(shù)據(jù)集的平均值:

# 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}))

到這里,我們已經(jīng)使用神經(jīng)網(wǎng)絡(luò)來創(chuàng)建分類的任務(wù)。值得慶賀一下!

可以在這里看到完整的代碼(可以修改我們定義的值來查看怎么樣影響訓(xùn)練時間和模型的精度):https://github.com/dmesquita/understanding_tensorflow_nn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容