中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

建設(shè)維護(hù)網(wǎng)站未簽訂合同網(wǎng)絡(luò)軟文寫作

建設(shè)維護(hù)網(wǎng)站未簽訂合同,網(wǎng)絡(luò)軟文寫作,網(wǎng)站做支付端口的費(fèi)用,十大食品公司原文:Deep Learning with TensorFlow Second Edition 協(xié)議:CC BY-NC-SA 4.0 譯者:飛龍 本文來(lái)自【ApacheCN 深度學(xué)習(xí) 譯文集】,采用譯后編輯(MTPE)流程來(lái)盡可能提升效率。 不要擔(dān)心自己的形象,只…

原文:Deep Learning with TensorFlow Second Edition

協(xié)議:CC BY-NC-SA 4.0

譯者:飛龍

本文來(lái)自【ApacheCN 深度學(xué)習(xí) 譯文集】,采用譯后編輯(MTPE)流程來(lái)盡可能提升效率。

不要擔(dān)心自己的形象,只關(guān)心如何實(shí)現(xiàn)目標(biāo)?!对瓌t》,生活原則 2.3.c

六、RNN 和梯度消失或爆炸問(wèn)題

較深層的梯度計(jì)算為多層網(wǎng)絡(luò)中許多激活函數(shù)梯度的乘積。當(dāng)這些梯度很小或?yàn)榱銜r(shí),它很容易消失。另一方面,當(dāng)它們大于 1 時(shí),它可能會(huì)爆炸。因此,計(jì)算和更新變得非常困難。

讓我們更詳細(xì)地解釋一下:

  • 如果權(quán)重較小,則可能導(dǎo)致稱為消失梯度的情況,其中梯度信號(hào)變得非常小,以至于學(xué)習(xí)變得非常慢或完全停止工作。這通常被稱為消失梯度。
  • 如果該矩陣中的權(quán)重很大,則可能導(dǎo)致梯度信號(hào)太大而導(dǎo)致學(xué)習(xí)發(fā)散的情況。這通常被稱為爆炸梯度。

因此,RNN 的一個(gè)主要問(wèn)題是消失或爆炸梯度問(wèn)題,它直接影響表現(xiàn)。事實(shí)上,反向傳播時(shí)間推出了 RNN,創(chuàng)建了一個(gè)非常深的前饋神經(jīng)網(wǎng)絡(luò)。從 RNN 獲得長(zhǎng)期背景的不可能性正是由于這種現(xiàn)象:如果梯度在幾層內(nèi)消失或爆炸,網(wǎng)絡(luò)將無(wú)法學(xué)習(xí)數(shù)據(jù)之間的高時(shí)間距離關(guān)系。

下圖顯示了發(fā)生的情況:計(jì)算和反向傳播的梯度趨于在每個(gè)時(shí)刻減少(或增加),然后,在一定數(shù)量的時(shí)刻之后,成本函數(shù)趨于收斂到零(或爆炸到無(wú)窮大) )。

我們可以通過(guò)兩種方式獲得爆炸梯度。由于激活函數(shù)的目的是通過(guò)壓縮它們來(lái)控制網(wǎng)絡(luò)中的重大變化,因此我們?cè)O(shè)置的權(quán)重必須是非負(fù)的和大的。當(dāng)這些權(quán)重沿著層次相乘時(shí),它們會(huì)導(dǎo)致成本的大幅變化。當(dāng)我們的神經(jīng)網(wǎng)絡(luò)模型學(xué)習(xí)時(shí),最終目標(biāo)是最小化成本函數(shù)并改變權(quán)重以達(dá)到最優(yōu)成本。

例如,成本函數(shù)是均方誤差。它是一個(gè)純凸函數(shù),目的是找到凸起的根本原因。如果你的權(quán)重增加到一定量,那么下降的時(shí)刻就會(huì)增加,我們會(huì)反復(fù)超過(guò)最佳狀態(tài),模型永遠(yuǎn)不會(huì)學(xué)習(xí)!

RNN and the gradient vanishing-exploding problem

在上圖中,我們有以下參數(shù):

  • θ表示隱藏的循環(huán)層的參數(shù)
  • θ[x]表示隱藏層的輸入?yún)?shù)
  • θ[y]表示輸出層的參數(shù)
  • σ表示隱藏層的激活函數(shù)
  • 輸入表示為X[t]
  • 隱藏層的輸出為h[t]
  • 最終輸出為o[t]
  • t(時(shí)間步長(zhǎng))

注意,上圖表示下面給出的循環(huán)神經(jīng)網(wǎng)絡(luò)模型的時(shí)間流逝。現(xiàn)在,如果你回憶一下圖 1,輸出可以表示如下:

RNN and the gradient vanishing-exploding problem

現(xiàn)在讓E代表輸出層的損失:E = f(O[t])。然后,上述三個(gè)方程告訴我們E取決于輸出O[t]。輸出O[t]相對(duì)于層的隱藏狀態(tài)(h[t])的變化而變化。當(dāng)前時(shí)間步長(zhǎng)(h[t])的隱藏狀態(tài)取決于先前時(shí)間步長(zhǎng)(h[t-1])的神經(jīng)元狀態(tài)。現(xiàn)在,下面的等式將清除這個(gè)概念。

相對(duì)于為隱藏層選擇的參數(shù)的損失變化率= ?E/?θ,這是一個(gè)可以表述如下的鏈規(guī)則:

RNN and the gradient vanishing-exploding problem

(I)

在前面的等式中,項(xiàng)?h[t]/?h[k]不僅有趣而且有用。

RNN and the gradient vanishing-exploding problem

(II)

現(xiàn)在,讓我們考慮t = 5k = 1然后

RNN and the gradient vanishing-exploding problem

(III)

微分方程(II)相對(duì)于(h[t-1])給出了:

RNN and the gradient vanishing-exploding problem

(IV)

現(xiàn)在,如果我們將方程(III)和(IV)結(jié)合起來(lái),我們可以得到以下結(jié)果:

RNN and the gradient vanishing-exploding problem

在這些情況下,θ也隨著時(shí)間步長(zhǎng)而變化。上面的等式顯示了當(dāng)前狀態(tài)相對(duì)于先前狀態(tài)的依賴性?,F(xiàn)在讓我們解釋這兩個(gè)方程的解剖。假設(shè)您處于時(shí)間步長(zhǎng) 5(t = 5),那么k的范圍從 1 到 5(k = 1到 5),這意味著您必須為以下內(nèi)容計(jì)算k):

RNN and the gradient vanishing-exploding problem

現(xiàn)在來(lái)看上面的每一個(gè)等式(II)

RNN and the gradient vanishing-exploding problem

而且,它取決于循環(huán)層的參數(shù)θ。如果在訓(xùn)練期間你的權(quán)重變大,那么由于每個(gè)時(shí)間步長(zhǎng)的等式(I)(II)的乘法,它們將會(huì)出現(xiàn)梯度爆炸的問(wèn)題。

為了克服消失或爆炸問(wèn)題,已經(jīng)提出了基本 RNN 模型的各種擴(kuò)展。將在下一節(jié)介紹的 LSTM 網(wǎng)絡(luò)就是其中之一。

LSTM 網(wǎng)絡(luò)

一種 RNN 模型是 LSTM。 LSTM 的精確實(shí)現(xiàn)細(xì)節(jié)不在本書的范圍內(nèi)。 LSTM 是一種特殊的 RNN 架構(gòu),最初由 Hochreiter 和 Schmidhuber 于 1997 年構(gòu)思。

最近在深度學(xué)習(xí)的背景下重新發(fā)現(xiàn)了這種類型的神經(jīng)網(wǎng)絡(luò),因?yàn)樗鼪](méi)有消失梯度的問(wèn)題,并且提供了出色的結(jié)果和表現(xiàn)?;?LSTM 的網(wǎng)絡(luò)是時(shí)間序列的預(yù)測(cè)和分類的理想選擇,并且正在取代許多傳統(tǒng)的深度學(xué)習(xí)方法。

這個(gè)名稱意味著短期模式不會(huì)被遺忘。 LSTM 網(wǎng)絡(luò)由彼此鏈接的單元(LSTM 塊)組成。每個(gè) LSTM 塊包含三種類型的門:輸入門,輸出門和遺忘門,它們分別實(shí)現(xiàn)對(duì)單元存儲(chǔ)器的寫入,讀取和復(fù)位功能。這些門不是二元的,而是模擬的(通常由映射在[0, 1]范圍內(nèi)的 Sigmoid 激活函數(shù)管理,其中 0 表示總抑制,1 表示總激活)。

如果你認(rèn)為 LSTM 單元是一個(gè)黑盒子,它可以像基本單元一樣使用,除了它會(huì)表現(xiàn)得更好;訓(xùn)練將更快地收斂,它將檢測(cè)數(shù)據(jù)中的長(zhǎng)期依賴性。在 TensorFlow 中,您只需使用BasicLSTMCell代替BasicRNNCell

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=n_neurons)

LSTM 單元管理兩個(gè)狀態(tài)向量,并且出于表現(xiàn)原因,它們默認(rèn)保持獨(dú)立。您可以通過(guò)在創(chuàng)建BasicLSTMCell時(shí)設(shè)置state_is_tuple=False來(lái)更改此默認(rèn)行為。那么,LSTM 單元如何工作?基本 LSTM 單元的架構(gòu)如下圖所示:

LSTM networks

圖 11:LSTM 單元的框圖

現(xiàn)在,讓我們看看這個(gè)架構(gòu)背后的數(shù)學(xué)符號(hào)。如果我們不查看 LSTM 框內(nèi)的內(nèi)容,LSTM 單元本身看起來(lái)就像常規(guī)存儲(chǔ)單元,除了它的狀態(tài)被分成兩個(gè)向量,h(t)c(t)

  • h(t)是短期狀態(tài)
  • c(t)是長(zhǎng)期狀態(tài)

現(xiàn)在,讓我們打開(kāi)盒子吧!關(guān)鍵的想法是網(wǎng)絡(luò)可以學(xué)習(xí)以下內(nèi)容:

  • 在長(zhǎng)期的狀態(tài)中存儲(chǔ)什么
  • 扔掉什么
  • 怎么讀它

由于長(zhǎng)期c(t)從左到右穿過(guò)網(wǎng)絡(luò),你可以看到它首先通過(guò)一個(gè)遺忘門,丟棄一些內(nèi)存,然后它添加一些新的存儲(chǔ)器通過(guò)加法運(yùn)算(增加了輸入門選擇的存儲(chǔ)器)。結(jié)果c(t)直接發(fā)送,沒(méi)有任何進(jìn)一步的變換

因此,在每個(gè)時(shí)間步驟,都會(huì)丟棄一些內(nèi)存并添加一些內(nèi)存。此外,在加法運(yùn)算之后,長(zhǎng)期狀態(tài)被復(fù)制并通過(guò) tanh 函數(shù),該函數(shù)產(chǎn)生[-1, +1]范圍內(nèi)的輸出。

然后輸出門過(guò)濾結(jié)果。這會(huì)產(chǎn)生短期h(t)(等于此時(shí)間步的單元輸出y(t))?,F(xiàn)在,讓我們來(lái)看看新記憶的來(lái)源以及大門如何運(yùn)作。首先,當(dāng)前輸入x(t)和之前的短路h(t-1)被饋送到四個(gè)不同的完全連接。這些門的存在允許 LSTM 單元無(wú)限期地記住信息:事實(shí)上,如果輸入門低于激活閾值,單元格將保持先前的狀態(tài),如果啟用當(dāng)前狀態(tài),它將與輸入值組合。顧名思義,遺忘門重置單元的當(dāng)前狀態(tài)(當(dāng)其值被清除為 0 時(shí)),輸出門決定是否必須執(zhí)行單元的值。

以下等式用于對(duì)單個(gè)實(shí)例的單元的長(zhǎng)期狀態(tài),其短期狀態(tài)及其在每個(gè)時(shí)間步的輸出進(jìn)行 LSTM 計(jì)算:

LSTM networks

在前面的方程中,W[xi]W[xf]W[xo]W[xg]是四個(gè)層中每個(gè)層的權(quán)重矩陣,用于與輸入向量x(t)連接。另一方面,W[hi]W[hf]W[ho],和W[hg]是四層中每一層的權(quán)重矩陣,它們與先前的短期狀態(tài)有關(guān)。b[i]、b[f]b[o]、b[g]是四層中每一層的偏差項(xiàng)。 TensorFlow 初始化它們?yōu)橐粋€(gè)全 1 的向量而不是全 0 的向量。這可以防止它在訓(xùn)練開(kāi)始時(shí)遺忘一切。

GRU 單元

LSTM 單元還有許多其他變體。一種特別流行的變體是門控循環(huán)單元(GRU)。 Kyunghyun Cho 和其他人在 2014 年的論文中提出了 GRU 單元,該論文還介紹了我們前面提到的自編碼器網(wǎng)絡(luò)。

從技術(shù)上講,GRU 單元是 LSTM 單元的簡(jiǎn)化版本,其中兩個(gè)狀態(tài)向量合并為一個(gè)稱為h(t)的向量。單個(gè)門控制器控制遺忘門和輸入門。如果門控制器的輸出為 1,則輸入門打開(kāi)并且遺忘門關(guān)閉。

GRU cell

圖 12:GRU 單元

另一方面,如果輸出為 0,則相反。每當(dāng)必須存儲(chǔ)存儲(chǔ)器時(shí),首先擦除存儲(chǔ)它的位置,這實(shí)際上是 LSTM 單元本身的常見(jiàn)變體。第二種簡(jiǎn)化是因?yàn)樵诿總€(gè)時(shí)間步輸出滿狀態(tài)向量,所以沒(méi)有輸出門。但是,新的門控制器控制先前狀態(tài)的哪一部分將顯示給主層。

以下等式用于為單個(gè)實(shí)例,在每個(gè)時(shí)間步計(jì)算 GRU 單元的長(zhǎng)期狀態(tài),其短期狀態(tài)及其輸出的:

GRU cell

在 TensorFlow 中創(chuàng)建 GRU 單元非常簡(jiǎn)單。這是一個(gè)例子:

gru_cell = tf.nn.rnn_cell.GRUCell(num_units=n_neurons)

這些簡(jiǎn)化并不是這種架構(gòu)的弱點(diǎn);它似乎成功地執(zhí)行。 LSTM 或 GRU 單元是近年來(lái) RNN 成功背后的主要原因之一,特別是在 NLP 中的應(yīng)用。

我們將在本章中看到使用 LSTM 的示例,但下一節(jié)將介紹使用 RNN 進(jìn)行垃圾郵件/火腿文本分類的示例。

實(shí)現(xiàn) RNN 進(jìn)行垃圾郵件預(yù)測(cè)

在本節(jié)中,我們將看到如何在 TensorFlow 中實(shí)現(xiàn) RNN 來(lái)預(yù)測(cè)文本中的垃圾郵件。

數(shù)據(jù)描述和預(yù)處理

將使用來(lái)自 UCI ML 倉(cāng)庫(kù)的流行垃圾數(shù)據(jù)集,可從此鏈接下載amcollection.zip。

該數(shù)據(jù)集包含來(lái)自多個(gè)電子郵件的文本,其中一些被標(biāo)記為垃圾郵件。在這里,我們將訓(xùn)練一個(gè)模型,該模型將學(xué)習(xí)僅使用電子郵件文本區(qū)分垃圾郵件和非垃圾郵件。讓我們開(kāi)始導(dǎo)入所需的庫(kù)和模型:

import os
import re
import io
import requests
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from zipfile import ZipFile
from tensorflow.python.framework import ops
import warnings

另外,如果您需要,我們可以停止打印由 TensorFlow 產(chǎn)生的警告:

warnings.filterwarnings("ignore")
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
ops.reset_default_graph()

現(xiàn)在,讓我們?yōu)閳D創(chuàng)建 TensorFlow 會(huì)話:

sess = tf.Session()

下一個(gè)任務(wù)是設(shè)置 RNN 參數(shù):

epochs = 300
batch_size = 250
max_sequence_length = 25
rnn_size = 10
embedding_size = 50
min_word_frequency = 10
learning_rate = 0.0001
dropout_keep_prob = tf.placeholder(tf.float32)

讓我們手動(dòng)下載數(shù)據(jù)集并將其存儲(chǔ)在temp目錄的text_data.txt文件中。首先,我們?cè)O(shè)置路徑:

data_dir = 'temp'
data_file = 'text_data.txt'
if not os.path.exists(data_dir):os.makedirs(data_dir)

現(xiàn)在,我們直接以壓縮格式下載數(shù)據(jù)集:

if not os.path.isfile(os.path.join(data_dir, data_file)):zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'r = requests.get(zip_url)z = ZipFile(io.BytesIO(r.content))file = z.read('SMSSpamCollection')

我們?nèi)匀恍枰袷交瘮?shù)據(jù):

    text_data = file.decode()text_data = text_data.encode('ascii',errors='ignore')text_data = text_data.decode().split('\n')

現(xiàn)在,在文本文件中存儲(chǔ)前面提到的目錄:

    with open(os.path.join(data_dir, data_file), 'w') as file_conn:for text in text_data:file_conn.write("{}\n".format(text))
else:text_data = []with open(os.path.join(data_dir, data_file), 'r') as file_conn:for row in file_conn:text_data.append(row)text_data = text_data[:-1]

讓我們分開(kāi)單詞長(zhǎng)度至少為 2 的單詞:

text_data = [x.split('\t') for x in text_data if len(x)>=1]
[text_data_target, text_data_train] = [list(x) for x in zip(*text_data)]

現(xiàn)在我們創(chuàng)建一個(gè)文本清理函數(shù):

def clean_text(text_string):text_string = re.sub(r'([^\s\w]|_|[0-9])+', '', text_string)text_string = " ".join(text_string.split())text_string = text_string.lower()return(text_string)

我們調(diào)用前面的方法來(lái)清理文本:

text_data_train = [clean_text(x) for x in text_data_train]

現(xiàn)在我們需要做一個(gè)最重要的任務(wù),即創(chuàng)建單詞嵌入 - 將文本更改為數(shù)字向量:

vocab_processor = tf.contrib.learn.preprocessing.VocabularyProcessor(max_sequence_length, min_frequency=min_word_frequency)
text_processed = np.array(list(vocab_processor.fit_transform(text_data_train)))

現(xiàn)在讓我們隨意改變數(shù)據(jù)集的平衡:

text_processed = np.array(text_processed)
text_data_target = np.array([1 if x=='ham' else 0 for x in text_data_target])
shuffled_ix = np.random.permutation(np.arange(len(text_data_target)))
x_shuffled = text_processed[shuffled_ix]
y_shuffled = text_data_target[shuffled_ix]

現(xiàn)在我們已經(jīng)改組了數(shù)據(jù),我們可以將數(shù)據(jù)分成訓(xùn)練和測(cè)試集:

ix_cutoff = int(len(y_shuffled)*0.75)
x_train, x_test = x_shuffled[:ix_cutoff], x_shuffled[ix_cutoff:]
y_train, y_test = y_shuffled[:ix_cutoff], y_shuffled[ix_cutoff:]
vocab_size = len(vocab_processor.vocabulary_)
print("Vocabulary size: {:d}".format(vocab_size))
print("Training set size: {:d}".format(len(y_train)))
print("Test set size: {:d}".format(len(y_test)))

以下是上述代碼的輸出:

>>>
Vocabulary size: 933
Training set size: 4180
Test set size: 1394

在我們開(kāi)始訓(xùn)練之前,讓我們?yōu)?TensorFlow 圖創(chuàng)建占位符:

x_data = tf.placeholder(tf.int32, [None, max_sequence_length])
y_output = tf.placeholder(tf.int32, [None])

讓我們創(chuàng)建嵌入:

embedding_mat = tf.get_variable("embedding_mat", shape=[vocab_size, embedding_size], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
embedding_output = tf.nn.embedding_lookup(embedding_mat, x_data)

現(xiàn)在是構(gòu)建我們的 RNN 的時(shí)候了。以下代碼定義了 RNN 單元:

cell = tf.nn.rnn_cell.BasicRNNCell(num_units = rnn_size)
output, state = tf.nn.dynamic_rnn(cell, embedding_output, dtype=tf.float32)
output = tf.nn.dropout(output, dropout_keep_prob)

現(xiàn)在讓我們定義從 RNN 序列獲取輸出的方法:

output = tf.transpose(output, [1, 0, 2])
last = tf.gather(output, int(output.get_shape()[0]) - 1)

接下來(lái),我們定義 RNN 的權(quán)重和偏置:

weight = bias = tf.get_variable("weight", shape=[rnn_size, 2], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
bias = tf.get_variable("bias", shape=[2], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)

然后定義logits輸出。它使用前面代碼中的權(quán)重和偏置:

logits_out = tf.nn.softmax(tf.matmul(last, weight) + bias)

現(xiàn)在我們定義每個(gè)預(yù)測(cè)的損失,以便稍后,它們可以為損失函數(shù)做出貢獻(xiàn):

losses = tf.nn.sparse_softmax_cross_entropy_with_logits_v2(logits=logits_out, labels=y_output)

然后我們定義損失函數(shù):

loss = tf.reduce_mean(losses)

我們現(xiàn)在定義每個(gè)預(yù)測(cè)的準(zhǔn)確率:

accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(logits_out, 1), tf.cast(y_output, tf.int64)), tf.float32))

然后我們用RMSPropOptimizer創(chuàng)建training_op

optimizer = tf.train.RMSPropOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

現(xiàn)在讓我們使用global_variables_initializer()方法初始化所有變量 :

init_op = tf.global_variables_initializer()
sess.run(init_op)

此外,我們可以創(chuàng)建一些空列表來(lái)跟蹤每個(gè)周期的訓(xùn)練損失,測(cè)試損失,訓(xùn)練準(zhǔn)確率和測(cè)試準(zhǔn)確率:

train_loss = []
test_loss = []
train_accuracy = []
test_accuracy = []

現(xiàn)在我們已準(zhǔn)備好進(jìn)行訓(xùn)練,讓我們開(kāi)始吧。訓(xùn)練的工作流程如下:

  • 打亂訓(xùn)練數(shù)據(jù)
  • 選擇訓(xùn)練集并計(jì)算周期
  • 為每個(gè)批次運(yùn)行訓(xùn)練步驟
  • 運(yùn)行損失和訓(xùn)練的準(zhǔn)確率
  • 運(yùn)行評(píng)估步驟。

以下代碼包括上述所有步驟:

    shuffled_ix = np.random.permutation(np.arange(len(x_train)))x_train = x_train[shuffled_ix]y_train = y_train[shuffled_ix]num_batches = int(len(x_train)/batch_size) + 1for i in range(num_batches):min_ix = i * batch_sizemax_ix = np.min([len(x_train), ((i+1) * batch_size)])x_train_batch = x_train[min_ix:max_ix]y_train_batch = y_train[min_ix:max_ix]train_dict = {x_data: x_train_batch, y_output: \
y_train_batch, dropout_keep_prob:0.5}sess.run(train_step, feed_dict=train_dict)temp_train_loss, temp_train_acc = sess.run([loss,\ accuracy], feed_dict=train_dict)train_loss.append(temp_train_loss)train_accuracy.append(temp_train_acc)test_dict = {x_data: x_test, y_output: y_test, \ 
dropout_keep_prob:1.0}temp_test_loss, temp_test_acc = sess.run([loss, accuracy], \feed_dict=test_dict)test_loss.append(temp_test_loss)test_accuracy.append(temp_test_acc)print('Epoch: {}, Test Loss: {:.2}, Test Acc: {:.2}'.format(epoch+1, temp_test_loss, temp_test_acc))
print('\nOverall accuracy on test set (%): {}'.format(np.mean(temp_test_acc)*100.0))

以下是前面代碼的輸出:

>>>
Epoch: 1, Test Loss: 0.68, Test Acc: 0.82
Epoch: 2, Test Loss: 0.68, Test Acc: 0.82
Epoch: 3, Test Loss: 0.67, Test Acc: 0.82
…
Epoch: 997, Test Loss: 0.36, Test Acc: 0.96
Epoch: 998, Test Loss: 0.36, Test Acc: 0.96
Epoch: 999, Test Loss: 0.35, Test Acc: 0.96
Epoch: 1000, Test Loss: 0.35, Test Acc: 0.96
Overall accuracy on test set (%): 96.19799256324768 

做得好! RNN 的準(zhǔn)確率高于 96%,非常出色?,F(xiàn)在讓我們觀察損失如何在每次迭代中傳播并隨著時(shí)間的推移:

epoch_seq = np.arange(1, epochs+1)
plt.plot(epoch_seq, train_loss, 'k--', label='Train Set')
plt.plot(epoch_seq, test_loss, 'r-', label='Test Set')
plt.title('RNN training/test loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend(loc='upper left')
plt.show()

Data description and preprocessing

圖 13:a)每個(gè)周期的 RNN 訓(xùn)練和測(cè)試損失 b)每個(gè)周期的測(cè)試精度

我們還隨時(shí)間繪制準(zhǔn)確率:

plt.plot(epoch_seq, train_accuracy, 'k--', label='Train Set')
plt.plot(epoch_seq, test_accuracy, 'r-', label='Test Set')
plt.title('Test accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='upper left')
plt.show()

下一個(gè)應(yīng)用使用時(shí)間序列數(shù)據(jù)進(jìn)行預(yù)測(cè)建模。我們還將看到如何開(kāi)發(fā)更復(fù)雜的 RNN,稱為 LSTM 網(wǎng)絡(luò)。

開(kāi)發(fā)時(shí)間序列數(shù)據(jù)的預(yù)測(cè)模型

RNN,特別是 LSTM 模型,通常是一個(gè)難以理解的主題。由于數(shù)據(jù)中的時(shí)間依賴性,時(shí)間序列預(yù)測(cè)是 RNN 的有用應(yīng)用。時(shí)間序列數(shù)據(jù)可在線獲取。在本節(jié)中,我們將看到使用 LSTM 處理時(shí)間序列數(shù)據(jù)的示例。我們的 LSTM 網(wǎng)絡(luò)將能夠預(yù)測(cè)未來(lái)的航空公司乘客數(shù)量。

數(shù)據(jù)集的描述

我將使用的數(shù)據(jù)集是 1949 年至 1960 年國(guó)際航空公司乘客的數(shù)據(jù)。該數(shù)據(jù)集可以從[此鏈接](https://datamarket.com/data/set/22u3/international-airlinepassengers- monthly-totals-in#!ds=22u3&display=line)。以下屏幕截圖顯示了國(guó)際航空公司乘客的元數(shù)據(jù):

Description of the dataset

圖 14:國(guó)際航空公司乘客的元數(shù)據(jù)(來(lái)源:https://datamarket.com/)

您可以通過(guò)選擇“導(dǎo)出”選項(xiàng)卡,然后在“導(dǎo)出”組中選擇 CSV 來(lái)下載數(shù)據(jù)。您必須手動(dòng)編輯 CSV 文件以刪除標(biāo)題行以及其他頁(yè)腳行。我已經(jīng)下載并保存了名為international-airline-passengers.csv的數(shù)據(jù)文件。下圖是時(shí)間序列數(shù)據(jù)的一個(gè)很好的圖:

Description of the dataset

圖 15:國(guó)際航空公司乘客:1 月 49 日至 12 月 60 日的月度總數(shù)為千人

預(yù)處理和探索性分析

現(xiàn)在讓我們加載原始數(shù)據(jù)集并查看一些事實(shí)。首先,我們加載時(shí)間序列如下(見(jiàn)time_series_preprocessor.py):

import csv
import numpy as np

在這里,我們可以看到load_series()的簽名,它是一個(gè)用戶定義的方法,可以加載時(shí)間序列并對(duì)其進(jìn)行正則化:

def load_series(filename, series_idx=1):try:with open(filename) as csvfile:csvreader = csv.reader(csvfile)data = [float(row[series_idx]) for row in csvreader if len(row) > 0]normalized_data = (data - np.mean(data)) / np.std(data)return normalized_dataexcept IOError:Print("Error occurred")return None

現(xiàn)在讓我們調(diào)用前面的方法加載時(shí)間序列并打印(在終端上發(fā)出$ python3 plot_time_series.py)數(shù)據(jù)集中的序列號(hào):

import csv
import numpy as np
import matplotlib.pyplot as plt
import time_series_preprocessor as tsp
timeseries = tsp.load_series('international-airline-passengers.csv')
print(timeseries)

以下是前面代碼的輸出:

>>>
[-1.40777884 -1.35759023 -1.24048348 -1.26557778 -1.33249593 -1.21538918-1.10664719 -1.10664719 -1.20702441 -1.34922546 -1.47469699 -1.35759023..2.85825285  2.72441656  1.9046693   1.5115252   0.91762667  1.26894693]
print(np.shape(timeseries))
>>>
144

這意味著時(shí)間序列中有144條目。讓我們繪制時(shí)間序列:

plt.figure()
plt.plot(timeseries)
plt.title('Normalized time series')
plt.xlabel('ID')
plt.ylabel('Normalized value')
plt.legend(loc='upper left')
plt.show()

以下是上述代碼的輸出:

>>>

Pre-processing and exploratory analysis

圖 16:時(shí)間序列(y 軸,標(biāo)準(zhǔn)化值與 x 軸,ID)

加載時(shí)間序列數(shù)據(jù)集后,下一個(gè)任務(wù)是準(zhǔn)備訓(xùn)練集。由于我們將多次評(píng)估模型以預(yù)測(cè)未來(lái)值,因此我們將數(shù)據(jù)分為訓(xùn)練和測(cè)試。更具體地說(shuō),split_data()函數(shù)將數(shù)據(jù)集劃分為兩個(gè)部分,用于訓(xùn)練和測(cè)試,75% 用于訓(xùn)練,25% 用于測(cè)試:

def split_data(data, percent_train):num_rows = len(data)train_data, test_data = [], []for idx, row in enumerate(data):if idx < num_rows * percent_train:train_data.append(row)else:test_data.append(row)return train_data, test_data

LSTM 預(yù)測(cè)模型

一旦我們準(zhǔn)備好數(shù)據(jù)集,我們就可以通過(guò)以可接受的格式加載數(shù)據(jù)來(lái)訓(xùn)練預(yù)測(cè)器。在這一步中,我編寫了一個(gè)名為TimeSeriesPredictor.py的 Python 腳本,它首先導(dǎo)入必要的庫(kù)和模塊(在此腳本的終端上發(fā)出$ python3 TimeSeriesPredictor.py命令):

import numpy as np
import tensorflow as tf
from tensorflow.python.ops import rnn, rnn_cell
import time_series_preprocessor as tsp
import matplotlib.pyplot as plt

接下來(lái),我們?yōu)?LSTM 網(wǎng)絡(luò)定義超參數(shù)(相應(yīng)地調(diào)整它):

input_dim = 1
seq_size = 5
hidden_dim = 5

我們現(xiàn)在定義權(quán)重變量(無(wú)偏差)和輸入占位符:

W_out = tf.get_variable("W_out", shape=[hidden_dim, 1], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None) 
b_out = tf.get_variable("b_out", shape=[1], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
x = tf.placeholder(tf.float32, [None, seq_size, input_dim])
y = tf.placeholder(tf.float32, [None, seq_size])

下一個(gè)任務(wù)是構(gòu)建 LSTM 網(wǎng)絡(luò)。以下方法LSTM_Model()采用三個(gè)參數(shù),如下所示:

  • x:大小為[T, batch_size, input_size]的輸入
  • W:完全連接的輸出層權(quán)重矩陣
  • b:完全連接的輸出層偏置向量

現(xiàn)在讓我們看一下方法的簽名:

def LSTM_Model():cell = rnn_cell.BasicLSTMCell(hidden_dim)outputs, states = rnn.dynamic_rnn(cell, x, dtype=tf.float32)num_examples = tf.shape(x)[0]W_repeated = tf.tile(tf.expand_dims(W_out, 0), [num_examples, 1, 1])out = tf.matmul(outputs, W_repeated) + b_outout = tf.squeeze(out)return out

此外,我們創(chuàng)建了三個(gè)空列表來(lái)存儲(chǔ)訓(xùn)練損失,測(cè)試損失和步驟:

train_loss = []
test_loss = []
step_list = []

下一個(gè)名為train()的方法用于訓(xùn)練 LSTM 網(wǎng)絡(luò):

def trainNetwork(train_x, train_y, test_x, test_y):with tf.Session() as sess:tf.get_variable_scope().reuse_variables()sess.run(tf.global_variables_initializer())max_patience = 3patience = max_patiencemin_test_err = float('inf')step = 0while patience > 0:_, train_err = sess.run([train_op, cost], feed_dict={x: train_x, y: train_y})if step % 100 == 0:test_err = sess.run(cost, feed_dict={x: test_x, y: test_y})print('step: {}\t\ttrain err: {}\t\ttest err: {}'.format(step, train_err, test_err))train_loss.append(train_err)test_loss.append(test_err)step_list.append(step)if test_err < min_test_err:min_test_err = test_errpatience = max_patienceelse:patience -= 1step += 1save_path = saver.save(sess, 'model.ckpt')print('Model saved to {}'.format(save_path))

接下來(lái)的任務(wù)是創(chuàng)建成本優(yōu)化器并實(shí)例化training_op

cost = tf.reduce_mean(tf.square(LSTM_Model()- y))
train_op = tf.train.AdamOptimizer(learning_rate=0.003).minimize(cost)

另外,這里有一個(gè)叫做保存模型的輔助op

saver = tf.train.Saver()

現(xiàn)在我們已經(jīng)創(chuàng)建了模型,下一個(gè)方法稱為testLSTM(),用于測(cè)試模型在測(cè)試集上的預(yù)測(cè)能力:

def testLSTM(sess, test_x):tf.get_variable_scope().reuse_variables()saver.restore(sess, 'model.ckpt')output = sess.run(LSTM_Model(), feed_dict={x: test_x})return output

為了繪制預(yù)測(cè)結(jié)果,我們有一個(gè)名為plot_results()的函數(shù)。簽名如下:

def plot_results(train_x, predictions, actual, filename):plt.figure()num_train = len(train_x)plt.plot(list(range(num_train)), train_x, color='b', label='training data')plt.plot(list(range(num_train, num_train + len(predictions))), predictions, color='r', label='predicted')plt.plot(list(range(num_train, num_train + len(actual))), actual, color='g', label='test data')plt.legend()if filename is not None:plt.savefig(filename)else:plt.show()

模型評(píng)估

為了評(píng)估模型,我們有一個(gè)名為main()的方法,它實(shí)際上調(diào)用前面的方法來(lái)創(chuàng)建和訓(xùn)練 LSTM 網(wǎng)絡(luò)。代碼的工作流程如下:

  1. 加載數(shù)據(jù)
  2. 在時(shí)間序列數(shù)據(jù)中滑動(dòng)窗口以構(gòu)建訓(xùn)練數(shù)據(jù)集
  3. 執(zhí)行相同的窗口滑動(dòng)策略來(lái)構(gòu)建測(cè)試數(shù)據(jù)集
  4. 在訓(xùn)練數(shù)據(jù)集上訓(xùn)練模型
  5. 可視化模型的表現(xiàn)

讓我們看看方法的簽名:

def main():data = tsp.load_series('international-airline-passengers.csv')train_data, actual_vals = tsp.split_data(data=data, percent_train=0.75)train_x, train_y = [], []for i in range(len(train_data) - seq_size - 1):train_x.append(np.expand_dims(train_data[i:i+seq_size], axis=1).tolist())train_y.append(train_data[i+1:i+seq_size+1])test_x, test_y = [], []for i in range(len(actual_vals) - seq_size - 1):test_x.append(np.expand_dims(actual_vals[i:i+seq_size], axis=1).tolist())test_y.append(actual_vals[i+1:i+seq_size+1])trainNetwork(train_x, train_y, test_x, test_y)with tf.Session() as sess:predicted_vals = testLSTM(sess, test_x)[:,0]# Following prediction results of the model given ground truth valuesplot_results(train_data, predicted_vals, actual_vals, 'ground_truth_predition.png')prev_seq = train_x[-1]predicted_vals = []for i in range(1000):next_seq = testLSTM(sess, [prev_seq])predicted_vals.append(next_seq[-1])prev_seq = np.vstack((prev_seq[1:], next_seq[-1]))# Following predictions results where only the training data was givenplot_results(train_data, predicted_vals, actual_vals, 'prediction_on_train_set.png')
>>>

最后,我們將調(diào)用main()方法來(lái)執(zhí)行訓(xùn)練。訓(xùn)練完成后,它進(jìn)一步繪制模型的預(yù)測(cè)結(jié)果,包括地面實(shí)況值與預(yù)測(cè)結(jié)果,其中只給出了訓(xùn)練數(shù)據(jù):

>>>

Model evaluation

圖 17:模型對(duì)地面實(shí)況值的結(jié)果

下圖顯示了訓(xùn)練數(shù)據(jù)的預(yù)測(cè)結(jié)果。此過(guò)程可用的信息較少,但它仍然可以很好地匹配數(shù)據(jù)中的趨勢(shì):

Model evaluation

圖 18:訓(xùn)練集上模型的結(jié)果

以下方法幫助我們繪制訓(xùn)練和測(cè)試誤差:

def plot_error():# Plot training loss over timeplt.plot(step_list, train_loss, 'r--', label='LSTM training loss per iteration', linewidth=4)plt.title('LSTM training loss per iteration')plt.xlabel('Iteration')plt.ylabel('Training loss')plt.legend(loc='upper right')plt.show()# Plot test loss over timeplt.plot(step_list, test_loss, 'r--', label='LSTM test loss per iteration', linewidth=4)plt.title('LSTM test loss per iteration')plt.xlabel('Iteration')plt.ylabel('Test loss')plt.legend(loc='upper left')plt.show()

現(xiàn)在我們調(diào)用上面的方法如下:

plot_error()
>>>

Model evaluation

圖 19:a)每次迭代的 LSTM 訓(xùn)練損失,b)每次迭代的 LSTM 測(cè)試損失

我們可以使用時(shí)間序列預(yù)測(cè)器來(lái)重現(xiàn)數(shù)據(jù)中的實(shí)際波動(dòng)?,F(xiàn)在,您可以準(zhǔn)備自己的數(shù)據(jù)集并執(zhí)行其他一些預(yù)測(cè)分析。下一個(gè)示例是關(guān)于產(chǎn)品和電影評(píng)論數(shù)據(jù)集的情感分析。我們還將了解如何使用 LSTM 網(wǎng)絡(luò)開(kāi)發(fā)更復(fù)雜的 RNN。

用于情感分析的 LSTM 預(yù)測(cè)模型

情感分析是 NLP 中使用最廣泛的任務(wù)之一。 LSTM 網(wǎng)絡(luò)可用于將短文本分類為期望的類別,即分類問(wèn)題。例如,一組推文可以分為正面或負(fù)面。在本節(jié)中,我們將看到這樣一個(gè)例子。

網(wǎng)絡(luò)設(shè)計(jì)

實(shí)現(xiàn)的 LSTM 網(wǎng)絡(luò)將具有三層:嵌入層,RNN 層和 softmax 層。從下圖可以看到對(duì)此的高級(jí)視圖。在這里,我總結(jié)了所有層的功能:

  • 嵌入層:我們將在第 8 章中看到一個(gè)示例,顯示文本數(shù)據(jù)集不能直接饋送到深度神經(jīng)網(wǎng)絡(luò)(DNN),因此一個(gè)名為嵌入層是必需的。對(duì)于該層,我們將每個(gè)輸入(k 個(gè)單詞的張量)變換為 k 個(gè) N 維向量的張量。這稱為字嵌入,其中 N 是嵌入大小。每個(gè)單詞都與在訓(xùn)練過(guò)程中需要學(xué)習(xí)的權(quán)重向量相關(guān)聯(lián)。您可以在單詞的向量表示中更深入地了解單詞嵌入。

  • RNN 層:一旦我們構(gòu)建了嵌入層,就會(huì)有一個(gè)名為 RNN 層的新層,它由帶有壓降包裝的 LSTM 單元組成。在訓(xùn)練過(guò)程中需要學(xué)習(xí) LSTM 權(quán)重,如前幾節(jié)所述。動(dòng)態(tài)展開(kāi) RNN 層(如圖 4 所示),將 k 個(gè)字嵌入作為輸入并輸出 k 個(gè) M 維向量,其中 M 是 LSTM 單元的隱藏大小。

  • Softmax 或 Sigmoid 層:RNN 層的輸出在k個(gè)時(shí)間步長(zhǎng)上平均,獲得大小為M的單個(gè)張量。最后,例如,softmax 層用于計(jì)算分類概率。

    Network design

    圖 20:用于情感分析的 LSTM 網(wǎng)絡(luò)的高級(jí)視圖

稍后我們將看到交叉熵如何用作損失函數(shù),RMSProp是最小化它的優(yōu)化器。

LSTM 模型訓(xùn)練

UMICH SI650 - 情感分類數(shù)據(jù)集(刪除了重復(fù))包含有關(guān)密歇根大學(xué)捐贈(zèng)的產(chǎn)品和電影評(píng)論的數(shù)據(jù),可以從此鏈接下載。在獲取令牌之前,已經(jīng)清除了不需要的或特殊的字符(參見(jiàn)data.csv文件)。

以下腳本還會(huì)刪除停用詞(請(qǐng)參閱data_preparation.py)。給出一些標(biāo)記為陰性或陽(yáng)性的樣本(1 為正面,0 為負(fù)面):

情感情感文本
1達(dá)芬奇密碼書真棒。
1我很喜歡達(dá)芬奇密碼。
0天哪,我討厭斷背山。
0我討厭哈利波特。

表 1:情感數(shù)據(jù)集的樣本

現(xiàn)在,讓我們看一下為此任務(wù)訓(xùn)練 LSTM 網(wǎng)絡(luò)的分步示例。首先,我們導(dǎo)入必要的模塊和包(執(zhí)行train.py文件):

from data_preparation import Preprocessing
from lstm_network import LSTM_RNN_Network
import tensorflow as tf
import pickle
import datetime
import time
import os
import matplotlib.pyplot as plt

在前面的導(dǎo)入聲明中,data_preparationlstm_network是兩個(gè)輔助 Python 腳本,用于數(shù)據(jù)集準(zhǔn)備和網(wǎng)絡(luò)設(shè)計(jì)。我們稍后會(huì)看到更多細(xì)節(jié)?,F(xiàn)在讓我們?yōu)?LSTM 定義參數(shù):

data_dir = 'data/' # Data directory containing 'data.csv'
stopwords_file = 'data/stopwords.txt' # Path to stopwords file
n_samples= None # Set n_samples=None to use the whole dataset# Directory where TensorFlow summaries will be stored'
summaries_dir= 'logs/'
batch_size = 100 #Batch size
train_steps = 1000 #Number of training steps
hidden_size= 75 # Hidden size of LSTM layer
embedding_size = 75 # Size of embeddings layer
learning_rate = 0.01
test_size = 0.2
dropout_keep_prob = 0.5 # Dropout keep-probability
sequence_len = None # Maximum sequence length
validate_every = 100 # Step frequency to validate

我相信前面的參數(shù)是不言自明的。下一個(gè)任務(wù)是準(zhǔn)備 TensorBoard 使用的摘要:

summaries_dir = '{0}/{1}'.format(summaries_dir, datetime.datetime.now().strftime('%d_%b_%Y-%H_%M_%S'))
train_writer = tf.summary.FileWriter(summaries_dir + '/train')
validation_writer = tf.summary.FileWriter(summaries_dir + '/validation')

現(xiàn)在讓我們準(zhǔn)備模型目錄:

model_name = str(int(time.time()))
model_dir = '{0}/{1}'.format(checkpoints_root, model_name)
if not os.path.exists(model_dir):os.makedirs(model_dir)

接下來(lái),讓我們準(zhǔn)備數(shù)據(jù)并構(gòu)建 TensorFlow 圖(參見(jiàn)data_preparation.py文件):

data_lstm = Preprocessing(data_dir=data_dir,stopwords_file=stopwords_file,sequence_len=sequence_len,test_size=test_size,val_samples=batch_size,n_samples=n_samples,random_state=100)

在前面的代碼段中,Preprocessing是一個(gè)繼續(xù)的類(詳見(jiàn)data_preparation.py)幾個(gè)函數(shù)和構(gòu)造器,它們幫助我們預(yù)處理訓(xùn)練和測(cè)試集以訓(xùn)練 LSTM 網(wǎng)絡(luò)。在這里,我提供了每個(gè)函數(shù)及其功能的代碼。

該類的構(gòu)造器初始化數(shù)據(jù)預(yù)處理器。此類提供了一個(gè)接口,用于將數(shù)據(jù)加載,預(yù)處理和拆分為訓(xùn)練,驗(yàn)證和測(cè)試集。它需要以下參數(shù):

  • data_dir:包含數(shù)據(jù)集文件data.csv的數(shù)據(jù)目錄,其中包含名為SentimentTextSentiment的列。
  • stopwords_file:可選。如果提供,它將丟棄原始數(shù)據(jù)中的每個(gè)停用詞。
  • sequence_len:可選。如果m是數(shù)據(jù)集中的最大序列長(zhǎng)度,則需要sequence_len >= m。如果sequence_lenNone,則會(huì)自動(dòng)分配給m。
  • n_samples:可選。它是從數(shù)據(jù)集加載的樣本數(shù)(對(duì)大型數(shù)據(jù)集很有用)。如果n_samplesNone,則將加載整個(gè)數(shù)據(jù)集(注意;如果數(shù)據(jù)集很大,則可能需要一段時(shí)間來(lái)預(yù)處理每個(gè)樣本)。
  • test_size:可選。0 < test_size < 1。它表示要包含在測(cè)試集中的數(shù)據(jù)集的比例(默認(rèn)值為0.2)。
  • val_samples:可選但可用于表示驗(yàn)證樣本的絕對(duì)數(shù)量(默認(rèn)為100)。
  • random_state:這是隨機(jī)種子的可選參數(shù),用于將數(shù)據(jù)分成訓(xùn)練,測(cè)試和驗(yàn)證集(默認(rèn)為0)。
  • ensure_preprocessed:可選。如果ensure_preprocessed=True,它確保數(shù)據(jù)集已經(jīng)過(guò)預(yù)處理(默認(rèn)為False)。

構(gòu)造器的代碼如下:

def __init__(self, data_dir, stopwords_file=None, sequence_len=None, n_samples=None, test_size=0.2, val_samples=100, random_state=0, ensure_preprocessed=False):self._stopwords_file = stopwords_fileself._n_samples = n_samplesself.sequence_len = sequence_lenself._input_file = os.path.join(data_dir, 'data.csv')self._preprocessed_file=os.path.join(data_dir,"preprocessed_"+str(n_samples)+ ".npz")self._vocab_file = os.path.join(data_dir,"vocab_" + str(n_samples) + ".pkl")self._tensors = Noneself._sentiments = Noneself._lengths = Noneself._vocab = Noneself.vocab_size = None# Prepare dataif os.path.exists(self._preprocessed_file)and os.path.exists(self._vocab_file):print('Loading preprocessed files ...')self.__load_preprocessed()else:if ensure_preprocessed:raise ValueError('Unable to findpreprocessed files.')print('Reading data ...')self.__preprocess()# Split data in train, validation and test setsindices = np.arange(len(self._sentiments))x_tv, self._x_test, y_tv, self._y_test,tv_indices, test_indices = train_test_split(self._tensors,self._sentiments,indices,test_size=test_size,random_state=random_state,stratify=self._sentiments[:, 0])self._x_train,self._x_val,self._y_train,self._y_val,train_indices,val_indices= train_test_split(x_tv, y_tv, tv_indices, test_size=val_samples,random_state = random_state,stratify=y_tv[:, 0])self._val_indices = val_indicesself._test_indices = test_indicesself._train_lengths = self._lengths[train_indices]self._val_lengths = self._lengths[val_indices]self._test_lengths = self._lengths[test_indices]self._current_index = 0self._epoch_completed = 0 

現(xiàn)在讓我們看看前面方法的簽名。我們從_preprocess()方法開(kāi)始,該方法從data_dir / data.csv加載數(shù)據(jù),預(yù)處理每個(gè)加載的樣本,并存儲(chǔ)中間文件以避免以后進(jìn)行預(yù)處理。工作流程如下:

  1. 加載數(shù)據(jù)
  2. 清理示例文本
  3. 準(zhǔn)備詞匯詞典
  4. 刪除最不常見(jiàn)的單詞(它們可能是語(yǔ)法錯(cuò)誤),將樣本編碼為張量,并根據(jù)sequence_len用零填充每個(gè)張量
  5. 保存中間文件
  6. 存儲(chǔ)樣本長(zhǎng)度以備將來(lái)使用

現(xiàn)在讓我們看看下面的代碼塊,它代表了前面的工作流程:

def __preprocess(self):data = pd.read_csv(self._input_file, nrows=self._n_samples)self._sentiments = np.squeeze(data.as_matrix(columns=['Sentiment']))self._sentiments = np.eye(2)[self._sentiments]samples = data.as_matrix(columns=['SentimentText'])[:, 0]samples = self.__clean_samples(samples)vocab = dict()vocab[''] = (0, len(samples))  # add empty wordfor sample in samples:sample_words = sample.split()for word in list(set(sample_words)):  # distinct wordsvalue = vocab.get(word)if value is None:vocab[word] = (-1, 1)else:encoding, count = valuevocab[word] = (-1, count + 1)sample_lengths = []tensors = []word_count = 1for sample in samples:sample_words = sample.split()encoded_sample = []for word in list(set(sample_words)):  # distinct words value = vocab.get(word)if value is not None:encoding, count = valueif count / len(samples) > 0.0001:if encoding == -1:encoding = word_countvocab[word] = (encoding, count)word_count += 1encoded_sample += [encoding]else:del vocab[word]tensors += [encoded_sample]sample_lengths += [len(encoded_sample)]self.vocab_size = len(vocab)self._vocab = vocabself._lengths = np.array(sample_lengths)self.sequence_len, self._tensors = self.__apply_to_zeros(tensors, self.sequence_len)with open(self._vocab_file, 'wb') as f:pickle.dump(self._vocab, f)np.savez(self._preprocessed_file, tensors=self._tensors, lengths=self._lengths, sentiments=self._sentiments)

接下來(lái),我們調(diào)用前面的方法并加載中間文件,避免數(shù)據(jù)預(yù)處理:

def __load_preprocessed(self):with open(self._vocab_file, 'rb') as f:self._vocab = pickle.load(f)self.vocab_size = len(self._vocab)load_dict = np.load(self._preprocessed_file)self._lengths = load_dict['lengths']self._tensors = load_dict['tensors']self._sentiments = load_dict['sentiments']self.sequence_len = len(self._tensors[0])

一旦我們預(yù)處理數(shù)據(jù)集,下一個(gè)任務(wù)就是清理樣本。工作流程如下:

  1. 準(zhǔn)備正則表達(dá)式模式。
  2. 清理每個(gè)樣本。
  3. 恢復(fù) HTML 字符。
  4. 刪除@users和 URL。
  5. 轉(zhuǎn)換為小寫。
  6. 刪除標(biāo)點(diǎn)符號(hào)。
  7. C替換C+(連續(xù)出現(xiàn)兩次以上的字符)
  8. 刪除停用詞。

現(xiàn)在讓我們以編程方式編寫上述步驟。為此,我們有以下函數(shù):

def __clean_samples(self, samples):print('Cleaning samples ...')ret = []reg_punct = '[' + re.escape(''.join(string.punctuation)) + ']'if self._stopwords_file is not None:stopwords = self.__read_stopwords()sw_pattern = re.compile(r'\b(' + '|'.join(stopwords) + r')\b')for sample in samples:text = html.unescape(sample)words = text.split()words = [word for word in words if not word.startswith('@') and not word.startswith('http://')]text = ' '.join(words)text = text.lower()text = re.sub(reg_punct, ' ', text)text = re.sub(r'([a-z])\1{2,}', r'\1', text)if stopwords is not None:text = sw_pattern.sub('', text)ret += [text]return ret

__apply_to_zeros()方法返回使用的padding_length和填充張量的 NumPy 數(shù)組。首先,它找到最大長(zhǎng)度m,并確保m>=sequence_len。然后根據(jù)sequence_len用零填充列表:

def __apply_to_zeros(self, lst, sequence_len=None):inner_max_len = max(map(len, lst))if sequence_len is not None:if inner_max_len > sequence_len:raise Exception('Error: Provided sequence length is not sufficient')else:inner_max_len = sequence_len
result = np.zeros([len(lst), inner_max_len], np.int32)
for i, row in enumerate(lst):for j, val in enumerate(row):result[i][j] = val
return inner_max_len, result

下一個(gè)任務(wù)是刪除所有停用詞(在data / StopWords.txt file中提供)。此方法返回停用詞列表:

def __read_stopwords(self):if self._stopwords_file is None:return Nonewith open(self._stopwords_file, mode='r') as f:stopwords = f.read().splitlines()return stopwords

next_batch()方法將batch_size>0作為包含的樣本數(shù),在完成周期后返回批量大小樣本(text_tensortext_targettext_length),并隨機(jī)抽取訓(xùn)練樣本:

def next_batch(self, batch_size):start = self._current_indexself._current_index += batch_sizeif self._current_index > len(self._y_train):self._epoch_completed += 1ind = np.arange(len(self._y_train))np.random.shuffle(ind)self._x_train = self._x_train[ind]self._y_train = self._y_train[ind]self._train_lengths = self._train_lengths[ind]start = 0self._current_index = batch_sizeend = self._current_indexreturn self._x_train[start:end], self._y_train[start:end], self._train_lengths[start:end]

然后使用稱為get_val_data()的下一個(gè)方法來(lái)獲取在訓(xùn)練期間使用的驗(yàn)證集。它接受原始文本并返回驗(yàn)證數(shù)據(jù)。默認(rèn)情況下,它返回original_textoriginal_samplestext_tensortext_targettext_length),否則返回text_tensortext_targettext_length

def get_val_data(self, original_text=False):if original_text:data = pd.read_csv(self._input_file, nrows=self._n_samples)samples = data.as_matrix(columns=['SentimentText'])[:, 0]return samples[self._val_indices], self._x_val, self._y_val, self._val_lengthsreturn self._x_val, self._y_val, self._val_lengths

最后, 是一個(gè)名為get_test_data()的附加方法,用于準(zhǔn)備將在模型評(píng)估期間使用的測(cè)試集:

    def get_test_data(self, original_text=False):if original_text:data = pd.read_csv(self._input_file, nrows=self._n_samples)samples = data.as_matrix(columns=['SentimentText'])[:, 0]return samples[self._test_indices], self._x_test, self._y_test, self._test_lengthsreturn self._x_test, self._y_test, self._test_lengths

現(xiàn)在我們準(zhǔn)備數(shù)據(jù),以便 LSTM 網(wǎng)絡(luò)可以提供它:

lstm_model = LSTM_RNN_Network(hidden_size=[hidden_size],vocab_size=data_lstm.vocab_size,embedding_size=embedding_size,max_length=data_lstm.sequence_len,learning_rate=learning_rate)

在前面的代碼段中,LSTM_RNN_Network是一個(gè)包含多個(gè)函數(shù)和構(gòu)造器的類,可幫助我們創(chuàng)建 LSTM 網(wǎng)絡(luò)。即將推出的構(gòu)造器構(gòu)建了 TensorFlow LSTM 模型。它需要以下參數(shù):

  • hidden_size:一個(gè)數(shù)組,保存 rnn 層的 LSTM 單元中的單元數(shù)
  • vocab_size:樣本中的詞匯量大小
  • embedding_size:將使用此大小的向量對(duì)單詞進(jìn)行編碼
  • max_length:輸入張量的最大長(zhǎng)度
  • n_classes:類別的數(shù)量
  • learning_rate:RMSProp 算法的學(xué)習(xí)率
  • random_state:丟棄的隨機(jī)狀態(tài)

構(gòu)造器的代碼如下:

def __init__(self, hidden_size, vocab_size, embedding_size, max_length, n_classes=2, learning_rate=0.01, random_state=None):# Build TensorFlow graphself.input = self.__input(max_length)self.seq_len = self.__seq_len()self.target = self.__target(n_classes)self.dropout_keep_prob = self.__dropout_keep_prob()self.word_embeddings = self.__word_embeddings(self.input, vocab_size, embedding_size, random_state)self.scores = self.__scores(self.word_embeddings, self.seq_len, hidden_size, n_classes, self.dropout_keep_prob,random_state)self.predict = self.__predict(self.scores)self.losses = self.__losses(self.scores, self.target)self.loss = self.__loss(self.losses)self.train_step = self.__train_step(learning_rate, self.loss)self.accuracy = self.__accuracy(self.predict, self.target)self.merged = tf.summary.merge_all()

下一個(gè)函數(shù)被稱為_input(),它采用一個(gè)名為max_length的參數(shù),它是輸入張量的最大長(zhǎng)度。然后它返回一個(gè)輸入占位符,其形狀為[batch_size, max_length],用于 TensorFlow 計(jì)算:

    def __input(self, max_length):return tf.placeholder(tf.int32, [None, max_length], name='input')

接下來(lái),_seq_len()函數(shù)返回一個(gè)形狀為[batch_size]的序列長(zhǎng)度占位符。它保持給定批次中每個(gè)張量的實(shí)際長(zhǎng)度,允許動(dòng)態(tài)序列長(zhǎng)度:

def __seq_len(self):return tf.placeholder(tf.int32, [None], name='lengths')

下一個(gè)函數(shù)稱為_target()。它需要一個(gè)名為n_classes的參數(shù),它包含分類類的數(shù)量。最后,它返回形狀為[batch_size, n_classes]的目標(biāo)占位符:

def __target(self, n_classes):return tf.placeholder(tf.float32, [None, n_classes], name='target')

_dropout_keep_prob()返回一個(gè)持有丟棄的占位符保持概率以減少過(guò)擬合:

def __dropout_keep_prob(self):return tf.placeholder(tf.float32, name='dropout_keep_prob')

_cell()方法用于構(gòu)建帶有壓差包裝器的 LSTM 單元。它需要以下參數(shù):

  • hidden_size:它是 LSTM 單元中的單元數(shù)
  • dropout_keep_prob:這表示持有丟棄保持概率的張量
  • seed:它是一個(gè)可選值,可確保丟棄包裝器的隨機(jī)狀態(tài)計(jì)算的可重現(xiàn)性。

最后,它返回一個(gè)帶有丟棄包裝器的 LSTM 單元:

def __cell(self, hidden_size, dropout_keep_prob, seed=None):lstm_cell = tf.nn.rnn_cell.LSTMCell(hidden_size, state_is_tuple=True)dropout_cell = tf.nn.rnn_cell.DropoutWrapper(lstm_cell, input_keep_prob=dropout_keep_prob, output_keep_prob = dropout_keep_prob, seed=seed)return dropout_cell

一旦我們創(chuàng)建了 LSTM 單元格,我們就可以創(chuàng)建輸入標(biāo)記的嵌入。為此,__word_embeddings()可以解決這個(gè)問(wèn)題。它構(gòu)建一個(gè)形狀為[vocab_size, embedding_size]的嵌入層,輸入?yún)?shù)如x,它是形狀[batch_size, max_length]的輸入。vocab_size是詞匯量大小,即可能出現(xiàn)在樣本中的可能單詞的數(shù)量。embedding_size是將使用此大小的向量表示的單詞,種子是可選的,但確保嵌入初始化的隨機(jī)狀態(tài)。

最后,它返回具有形狀[batch_size, max_length, embedding_size]的嵌入查找張量:

def __word_embeddings(self, x, vocab_size, embedding_size, seed=None):with tf.name_scope('word_embeddings'):embeddings = tf.get_variable("embeddings",shape=[vocab_size, embedding_size], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)embedded_words = tf.nn.embedding_lookup(embeddings, x)return embedded_words

__rnn_layer ()方法創(chuàng)建 LSTM 層。它需要幾個(gè)輸入?yún)?shù),這里描述:

  • hidden_size:這是 LSTM 單元中的單元數(shù)
  • x:這是帶形狀的輸入
  • seq_len:這是具有形狀的序列長(zhǎng)度張量
  • dropout_keep_prob:這是持有丟棄保持概率的張量
  • variable_scope:這是變量范圍的名稱(默認(rèn)層是rnn_layer
  • random_state:這是丟棄包裝器的隨機(jī)狀態(tài)

最后,它返回形狀為[batch_size, max_seq_len, hidden_size]的輸出:

def __rnn_layer(self, hidden_size, x, seq_len, dropout_keep_prob, variable_scope=None, random_state=None):with tf.variable_scope(variable_scope, default_name='rnn_layer'):lstm_cell = self.__cell(hidden_size, dropout_keep_prob, random_state)outputs, _ = tf.nn.dynamic_rnn(lstm_cell, x, dtype=tf.float32, sequence_length=seq_len)return outputs

_score()方法用于計(jì)算網(wǎng)絡(luò)輸出。它需要幾個(gè)輸入?yún)?shù),如下所示:

  • embedded_words:這是具有形狀[batch_size, max_length, embedding_size]的嵌入查找張量
  • seq_len:這是形狀[batch_size]的序列長(zhǎng)度張量
  • hidden_size:這是一個(gè)數(shù)組,其中包含每個(gè) RNN 層中 LSTM 單元中的單元數(shù)
  • n_classes:這是類別的數(shù)量
  • dropout_keep_prob:這是持有丟棄保持概率的張量
  • random_state:這是一個(gè)可選參數(shù),但它可用于確保丟棄包裝器的隨機(jī)狀態(tài)

最后,_score()方法返回具有形狀[batch_size, n_classes]的每個(gè)類的線性激活:

def __scores(self, embedded_words, seq_len, hidden_size, n_classes, dropout_keep_prob, random_state=None):outputs = embedded_wordsfor h in hidden_size:outputs = self.__rnn_layer(h, outputs, seq_len, dropout_keep_prob)outputs = tf.reduce_mean(outputs, axis=[1])with tf.name_scope('final_layer/weights'):w = tf.get_variable("w", shape=[hidden_size[-1], n_classes], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)self.variable_summaries(w, 'final_layer/weights')with tf.name_scope('final_layer/biases'):b = tf.get_variable("b", shape=[n_classes], dtype=tf.float32, initializer=None, regularizer=None,trainable=True, collections=None)self.variable_summaries(b, 'final_layer/biases')with tf.name_scope('final_layer/wx_plus_b'):scores = tf.nn.xw_plus_b(outputs, w, b, name='scores')tf.summary.histogram('final_layer/wx_plus_b', scores)return scores

_predict()方法將得分作為具有形狀[batch_size, n_classes]的每個(gè)類的線性激活,并以形狀[batch_size, n_classes]返回 softmax(以[0, 1]的比例標(biāo)準(zhǔn)化得分)激活:

def __predict(self, scores):with tf.name_scope('final_layer/softmax'):softmax = tf.nn.softmax(scores, name='predictions')tf.summary.histogram('final_layer/softmax', softmax)return softmax

_losses()方法返回具有形狀[batch_size]的交叉熵?fù)p失(因?yàn)?softmax 用作激活函數(shù))。它還需要兩個(gè)參數(shù),例如得分,作為具有形狀[batch_size, n_classes]的每個(gè)類的線性激活和具有形狀[batch_size, n_classes]的目標(biāo)張量:

def __losses(self, scores, target):with tf.name_scope('cross_entropy'):cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=scores, labels=target, name='cross_entropy')return cross_entropy

_loss()函數(shù)計(jì)算并返回平均交叉熵?fù)p失。它只需要一個(gè)參數(shù),稱為損耗,它表示形狀[batch_size]的交叉熵?fù)p失,并由前一個(gè)函數(shù)計(jì)算:

def __loss(self, losses):with tf.name_scope('loss'):loss = tf.reduce_mean(losses, name='loss')tf.summary.scalar('loss', loss)return loss

現(xiàn)在,_train_step()計(jì)算并返回RMSProp訓(xùn)練步驟操作。它需要兩個(gè)參數(shù),learning_rate,這是RMSProp優(yōu)化器的學(xué)習(xí)率;和前一個(gè)函數(shù)計(jì)算的平均交叉熵?fù)p失:

def __train_step(self, learning_rate, loss):return tf.train.RMSPropOptimizer(learning_rate).minimize(loss)

評(píng)估表現(xiàn)時(shí),_accuracy()函數(shù)計(jì)算分類的準(zhǔn)確率。它需要三個(gè)參數(shù),預(yù)測(cè),softmax 激活具有哪種形狀[batch_size, n_classes];和具有形狀[batch_size, n_classes]的目標(biāo)張量和當(dāng)前批次中獲得的平均精度:

def __accuracy(self, predict, target):with tf.name_scope('accuracy'):correct_pred = tf.equal(tf.argmax(predict, 1), tf.argmax(target, 1))accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')tf.summary.scalar('accuracy', accuracy)return accuracy

下一個(gè)函數(shù)被稱為initialize_all_variable(),正如您可能猜到的那樣,它初始化所有變量:

def initialize_all_variables(self):return tf.global_variables_initializer()

最后,我們有一個(gè)名為variable_summaries()的靜態(tài)方法,它將大量摘要附加到 TensorBoard 可視化的張量上。它需要以下參數(shù):

var: is the variable to summarize
mean: mean of the summary name.

簽名如下:

    @staticmethoddef variable_summaries(var, name):with tf.name_scope('summaries'):mean = tf.reduce_mean(var)tf.summary.scalar('mean/' + name, mean)with tf.name_scope('stddev'):stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))tf.summary.scalar('stddev/' + name, stddev)tf.summary.scalar('max/' + name, tf.reduce_max(var))tf.summary.scalar('min/' + name, tf.reduce_min(var))tf.summary.histogram(name, var)

現(xiàn)在我們需要在訓(xùn)練模型之前創(chuàng)建一個(gè) TensorFlow 會(huì)話:

sess = tf.Session()

讓我們初始化所有變量:

init_op = tf.global_variables_initializer()
sess.run(init_op)

然后我們保存 TensorFlow 模型以備將來(lái)使用:

saver = tf.train.Saver()

現(xiàn)在讓我們準(zhǔn)備訓(xùn)練集:

x_val, y_val, val_seq_len = data_lstm.get_val_data()

現(xiàn)在我們應(yīng)該編寫 TensorFlow 圖計(jì)算的日志:

train_writer.add_graph(lstm_model.input.graph)

此外,我們可以創(chuàng)建一些空列表來(lái)保存訓(xùn)練損失,驗(yàn)證損失和步驟,以便我們以圖形方式查看它們:

train_loss_list = []
val_loss_list = []
step_list = []
sub_step_list = []
step = 0

現(xiàn)在我們開(kāi)始訓(xùn)練。在每個(gè)步驟中,我們記錄訓(xùn)練誤差。驗(yàn)證誤差記錄在每個(gè)子步驟中:

for i in range(train_steps):x_train, y_train, train_seq_len = data_lstm.next_batch(batch_size)train_loss, _, summary = sess.run([lstm_model.loss, lstm_model.train_step, lstm_model.merged],feed_dict={lstm_model.input: x_train,lstm_model.target: y_train,lstm_model.seq_len: train_seq_len,lstm_model.dropout_keep_prob:dropout_keep_prob})train_writer.add_summary(summary, i)  # Write train summary for step i (TensorBoard visualization)train_loss_list.append(train_loss)step_list.append(i)print('{0}/{1} train loss: {2:.4f}'.format(i + 1, FLAGS.train_steps, train_loss))if (i + 1) %validate_every == 0:val_loss, accuracy, summary = sess.run([lstm_model.loss, lstm_model.accuracy, lstm_model.merged],feed_dict={lstm_model.input: x_val,lstm_model.target: y_val,lstm_model.seq_len: val_seq_len,lstm_model.dropout_keep_prob: 1})validation_writer.add_summary(summary, i)  print('   validation loss: {0:.4f} (accuracy {1:.4f})'.format(val_loss, accuracy))step = step + 1val_loss_list.append(val_loss)sub_step_list.append(step)

以下是上述代碼的輸出:

>>>1/1000 train loss: 0.6883
2/1000 train loss: 0.6879
3/1000 train loss: 0.694399/1000 train loss: 0.4870
100/1000 train loss: 0.5307
validation loss: 0.4018 (accuracy 0.9200)199/1000 train loss: 0.1103
200/1000 train loss: 0.1032
validation loss: 0.0607 (accuracy 0.9800)299/1000 train loss: 0.0292
300/1000 train loss: 0.0266
validation loss: 0.0417 (accuracy 0.9800)998/1000 train loss: 0.0021
999/1000 train loss: 0.0007
1000/1000 train loss: 0.0004
validation loss: 0.0939 (accuracy 0.9700)

上述代碼打印了訓(xùn)練和驗(yàn)證誤差。訓(xùn)練結(jié)束后,模型將保存到具有唯一 ID 的檢查點(diǎn)目錄中:

checkpoint_file = '{}/model.ckpt'.format(model_dir)
save_path = saver.save(sess, checkpoint_file)
print('Model saved in: {0}'.format(model_dir))

以下是上述代碼的輸出:

>>>
Model saved in checkpoints/1517781236

檢查點(diǎn)目錄將至少生成三個(gè)文件:

  • config.pkl包含用于訓(xùn)練模型的參數(shù)。
  • model.ckpt包含模型的權(quán)重。
  • model.ckpt.meta包含 TensorFlow 圖定義。

讓我們看看訓(xùn)練是如何進(jìn)行的,也就是說(shuō),訓(xùn)練和驗(yàn)證損失如下:

# Plot loss over time
plt.plot(step_list, train_loss_list, 'r--', label='LSTM training loss per iteration', linewidth=4)
plt.title('LSTM training loss per iteration')
plt.xlabel('Iteration')
plt.ylabel('Training loss')
plt.legend(loc='upper right')
plt.show()# Plot accuracy over time
plt.plot(sub_step_list, val_loss_list, 'r--', label='LSTM validation loss per validating interval', linewidth=4)
plt.title('LSTM validation loss per validation interval')
plt.xlabel('Validation interval')
plt.ylabel('Validation loss')
plt.legend(loc='upper left')
plt.show()

以下是上述代碼的輸出:

>>>

LSTM model training

圖 21:a)測(cè)試集上每次迭代的 LSTM 訓(xùn)練損失,b)每個(gè)驗(yàn)證間隔的 LSTM 驗(yàn)證損失

如果我們檢查前面的繪圖,很明顯訓(xùn)練階段和驗(yàn)證階段的訓(xùn)練都很順利,只有 1000 步。然而,讀者應(yīng)加大訓(xùn)練步驟,調(diào)整超參數(shù),看看它是如何去。

通過(guò) TensorBoard 的可視化

現(xiàn)在讓我們觀察 TensorBoard 上的 TensorFlow 計(jì)算圖。只需執(zhí)行以下命令并在localhost:6006/訪問(wèn) TensorBoard:

tensorboard --logdir /home/logs/

圖選項(xiàng)卡顯示執(zhí)行圖,包括使用的梯度,loss_op,精度,最終層,使用的優(yōu)化器(在我們的例子中是RMSPro),LSTM 層(即 RNN 層),嵌入層和save_op

Visualizing through TensorBoard

圖 22:TensorBoard 上的執(zhí)行圖

執(zhí)行圖顯示,我們?yōu)檫@種基于 LSTM 的分類器進(jìn)行的情感分析計(jì)算是非常透明的。我們還可以觀察層中的驗(yàn)證,訓(xùn)練損失,準(zhǔn)確率和操作:

Visualizing through TensorBoard

圖 23:TensorBoard 層中的驗(yàn)證,訓(xùn)練損失,準(zhǔn)確率和操作

LSTM 模型評(píng)估

我們已經(jīng)訓(xùn)練了并保存了我們的 LSTM 模型。我們可以輕松恢復(fù)訓(xùn)練模型并進(jìn)行一些評(píng)估。我們需要準(zhǔn)備測(cè)試集并使用先前訓(xùn)練的 TensorFlow 模型對(duì)其進(jìn)行預(yù)測(cè)。我們馬上做吧。首先,我們加載所需的模型:

import tensorflow as tf
from data_preparation import Preprocessingimport pickle
Then we load to show the checkpoint directory where the model was saved. For our case, it was checkpoints/1505148083.

注意

對(duì)于此步驟,使用以下命令執(zhí)行predict.py腳本:

$ python3 predict.py --checkpoints_dir checkpoints/1517781236
# Change this path based on output by 'python3 train.py' 
checkpoints_dir = 'checkpoints/1517781236' ifcheckpoints_dir is None:raise ValueError('Please, a valid checkpoints directory is required (--checkpoints_dir <file name>)')

現(xiàn)在加載測(cè)試數(shù)據(jù)集并準(zhǔn)備它以評(píng)估模型:

data_lstm = Preprocessing(data_dir=data_dir,stopwords_file=stopwords_file,sequence_len=sequence_len,n_samples=n_samples,test_size=test_size,val_samples=batch_size,random_state=random_state,ensure_preprocessed=True)

在上面的代碼中,完全按照我們?cè)谟?xùn)練步驟中的操作使用以下參數(shù):

data_dir = 'data/' # Data directory containing 'data.csv'
stopwords_file = 'data/stopwords.txt' # Path to stopwords file.
sequence_len = None # Maximum sequence length
n_samples= None # Set n_samples=None to use the whole dataset
test_size = 0.2
batch_size = 100 #Batch size
random_state = 0 # Random state used for data splitting. Default is 0

此評(píng)估方法的工作流程如下:

  1. 首先,導(dǎo)入元圖并使用測(cè)試數(shù)據(jù)評(píng)估模型
  2. 為計(jì)算創(chuàng)建 TensorFlow 會(huì)話
  3. 導(dǎo)入圖并恢復(fù)其權(quán)重
  4. 恢復(fù)輸入/輸出張量
  5. 執(zhí)行預(yù)測(cè)
  6. 最后,我們?cè)诤?jiǎn)單的測(cè)試集上打印精度和結(jié)果

步驟 1 之前已經(jīng)完成 。此代碼執(zhí)行步驟 2 到 5:

original_text, x_test, y_test, test_seq_len = data_lstm.get_test_data(original_text=True)
graph = tf.Graph()
with graph.as_default():sess = tf.Session()    print('Restoring graph ...')saver = tf.train.import_meta_graph("{}/model.ckpt.meta".format(FLAGS.checkpoints_dir))saver.restore(sess, ("{}/model.ckpt".format(checkpoints_dir)))input = graph.get_operation_by_name('input').outputs[0]target = graph.get_operation_by_name('target').outputs[0]seq_len = graph.get_operation_by_name('lengths').outputs[0]dropout_keep_prob = graph.get_operation_by_name('dropout_keep_prob').outputs[0]predict = graph.get_operation_by_name('final_layer/softmax/predictions').outputs[0]accuracy = graph.get_operation_by_name('accuracy/accuracy').outputs[0]pred, acc = sess.run([predict, accuracy],feed_dict={input: x_test,target: y_test,seq_len: test_seq_len,dropout_keep_prob: 1})print("Evaluation done.")

以下是上述代碼的輸出:

>>>
Restoring graph ...
The evaluation was done.

做得好!訓(xùn)練結(jié)束了,讓我們打印結(jié)果:

print('\nAccuracy: {0:.4f}\n'.format(acc))
for i in range(100):print('Sample: {0}'.format(original_text[i]))print('Predicted sentiment: [{0:.4f}, {1:.4f}]'.format(pred[i, 0], pred[i, 1]))print('Real sentiment: {0}\n'.format(y_test[i]))

以下是上述代碼的輸出:

>>>
Accuracy: 0.9858Sample: I loved the Da Vinci code, but it raises many theological questions most of which are very absurd...
Predicted sentiment: [0.0000, 1.0000]
Real sentiment: [0\. 1.]
…
Sample: I'm sorry I hate to read Harry Potter, but I love the movies!
Predicted sentiment: [1.0000, 0.0000]
Real sentiment: [1\. 0.]
…
Sample: I LOVE Brokeback Mountain...
Predicted sentiment: [0.0002, 0.9998]
Real sentiment: [0\. 1.]
…
Sample: We also went to see Brokeback Mountain which totally SUCKED!!!
Predicted sentiment: [1.0000, 0.0000]
Real sentiment: [1\. 0.]

精度高于 98%。這太棒了!但是,您可以嘗試使用調(diào)整的超參數(shù)迭代訓(xùn)練以獲得更高的迭代次數(shù),您可能會(huì)獲得更高的準(zhǔn)確率。我把它留給讀者。

在下一節(jié)中,我們將看到如何使用 LSTM 開(kāi)發(fā)更高級(jí)的 ML 項(xiàng)目,這被稱為使用智能手機(jī)數(shù)據(jù)集的人類活動(dòng)識(shí)別。簡(jiǎn)而言之,我們的 ML 模型將能夠?qū)⑷祟愡\(yùn)動(dòng)分為六類:走路,走樓上,走樓下,坐,站立和鋪設(shè)。

LSTM 模型和人類活動(dòng)識(shí)別

人類活動(dòng)識(shí)別(HAR)數(shù)據(jù)庫(kù)是通過(guò)對(duì)攜帶帶有嵌入式慣性傳感器的腰部智能手機(jī)的 30 名參加日常生活活動(dòng)(ADL)的參與者進(jìn)行測(cè)量而建立的。目標(biāo)是將他們的活動(dòng)分類為前面提到的六個(gè)類別之一。

數(shù)據(jù)集描述

實(shí)驗(yàn)在一組 30 名志愿者中進(jìn)行,年齡范圍為 19-48 歲。每個(gè)人都在腰上戴著三星 Galaxy S II 智能手機(jī),完成了六項(xiàng)活動(dòng)(步行,走樓上,走樓下,坐著,站著,躺著)。使用加速度計(jì)和陀螺儀,作者以 50 Hz 的恒定速率捕獲了 3 軸線性加速度和 3 軸角速度。

僅使用兩個(gè)傳感器,加速度計(jì)和陀螺儀。通過(guò)應(yīng)用噪聲濾波器對(duì)傳感器信號(hào)進(jìn)行預(yù)處理,然后在 2.56 秒的固定寬度滑動(dòng)窗口中采樣,重疊 50%。這樣每個(gè)窗口提供 128 個(gè)讀數(shù)。來(lái)自傳感器加速度信號(hào)的重力和身體運(yùn)動(dòng)分量通過(guò)巴特沃斯低通濾波器分離成身體加速度和重力。

欲了解更多信息,請(qǐng)參閱本文:Davide Anguita,Alessandro Ghio,Luca Oneto,Xavier Parra 和 Jorge L. Reyes-Ortiz,使用智能手機(jī)進(jìn)行人類活動(dòng)識(shí)別的公共領(lǐng)域數(shù)據(jù)集和第 21 屆關(guān)于人工神經(jīng)網(wǎng)絡(luò)的歐洲研討會(huì),計(jì)算智能和機(jī)器學(xué)習(xí),ESANN 2013.比利時(shí)布魯日 24-26,2013 年 4 月。

為簡(jiǎn)單起見(jiàn),假設(shè)重力僅具有少量低頻分量。因此,使用 0.3Hz 截止頻率的濾波器。從每個(gè)窗口,通過(guò)計(jì)算來(lái)自時(shí)域和頻域的變量找到特征向量。

已經(jīng)對(duì)實(shí)驗(yàn)進(jìn)行了視頻記錄以便于手動(dòng)標(biāo)記數(shù)據(jù)。數(shù)據(jù)集已被隨機(jī)分為兩組,其中 70% 的志愿者被選中用于訓(xùn)練數(shù)據(jù),30% 用于測(cè)試數(shù)據(jù)。當(dāng)我瀏覽數(shù)據(jù)集時(shí),訓(xùn)練集和測(cè)試集都具有以下文件結(jié)構(gòu):

Dataset description

圖 24:HAR 數(shù)據(jù)集文件結(jié)構(gòu)

對(duì)于數(shù)據(jù)集中的每條記錄,提供以下內(nèi)容:

  • 來(lái)自加速度計(jì)的三軸加速度和估計(jì)的車身加速度
  • 來(lái)自陀螺儀傳感器的三軸角速度
  • 具有時(shí)域和頻域變量的 561 特征向量
  • 它的活動(dòng)標(biāo)簽
  • 進(jìn)行實(shí)驗(yàn)的受試者的標(biāo)識(shí)符

因此,我們知道需要解決的問(wèn)題?,F(xiàn)在是探索技術(shù)和相關(guān)挑戰(zhàn)的時(shí)候了。

用于 HAR 的 LSTM 模型的工作流程

整個(gè)算法有以下工作流程:

  1. 加載數(shù)據(jù)。
  2. 定義超參數(shù)。
  3. 使用命令式編程和超參數(shù)設(shè)置 LSTM 模型。
  4. 應(yīng)用批量訓(xùn)練。也就是說(shuō),選擇一批數(shù)據(jù),將其提供給模型,然后在一些迭代之后,評(píng)估模型并打印批次損失和準(zhǔn)確率。
  5. 輸出圖的訓(xùn)練和測(cè)試誤差。

可以遵循上述步驟并構(gòu)建一個(gè)管道:

Workflow of the LSTM model for HAR

圖 25:用于 HAR 的基于 LSTM 的管道

為 HAR 實(shí)現(xiàn) LSTM 模型

首先,我們導(dǎo)入所需的包和模塊:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn import metrics
from tensorflow.python.framework import ops
import warnings
import random
warnings.filterwarnings("ignore")
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

如前所述,INPUT_SIGNAL_TYPES包含一些有用的常量。它們是神經(jīng)網(wǎng)絡(luò)的單獨(dú)標(biāo)準(zhǔn)化輸入特征:

INPUT_SIGNAL_TYPES = ["body_acc_x_","body_acc_y_","body_acc_z_","body_gyro_x_","body_gyro_y_","body_gyro_z_","total_acc_x_","total_acc_y_","total_acc_z_"
]

標(biāo)簽在另一個(gè)數(shù)組中定義 - 這是用于學(xué)習(xí)如何分類的輸出類:

LABELS = ["WALKING","WALKING_UPSTAIRS","WALKING_DOWNSTAIRS","SITTING","STANDING","LAYING"
]

我們現(xiàn)在假設(shè)您已經(jīng)從[此鏈接](https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI HAR Dataset.zip)下載了 HAR 數(shù)據(jù)集并輸入了名為UCIHARDataset的文件夾(或者您可以選擇聽(tīng)起來(lái)更合適的合適名稱)。此外,我們需要提供訓(xùn)練和測(cè)試集的路徑:

DATASET_PATH = "UCIHARDataset/"
print("\n" + "Dataset is now located at: " + DATASET_PATH)TRAIN = "train/"
TEST = "test/"

然后我們加載并根據(jù)由[Array [Array [Float]]]格式的INPUT_SIGNAL_TYPES數(shù)組定義的輸入信號(hào)類型,映射每個(gè).txt文件的數(shù)據(jù)。X表示神經(jīng)網(wǎng)絡(luò)的訓(xùn)練和測(cè)試輸入:

def load_X(X_signals_paths):X_signals = []for signal_type_path in X_signals_paths:file = open(signal_type_path, 'r')# Read dataset from disk, dealing with text files' syntaxX_signals.append([np.array(serie, dtype=np.float32) for serie in [row.replace('  ', ' ').strip().split(' ') for row in file]])file.close()return np.transpose(np.array(X_signals), (1, 2, 0))X_train_signals_paths = [DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES]
X_test_signals_paths = [DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES]X_train = load_X(X_train_signals_paths)
X_test = load_X(X_test_signals_paths)

然后我們加載y,神經(jīng)網(wǎng)絡(luò)的訓(xùn)練和測(cè)試輸出的標(biāo)簽:

def load_y(y_path):file = open(y_path, 'r')# Read dataset from disk, dealing with text file's syntaxy_ = np.array([elem for elem in [row.replace('  ', ' ').strip().split(' ') for row in file]],dtype=np.int32)file.close()# We subtract 1 to each output class for 0-based indexing return y_ - 1y_train_path = DATASET_PATH + TRAIN + "y_train.txt"
y_test_path = DATASET_PATH + TEST + "y_test.txt"y_train = load_y(y_train_path)
y_test = load_y(y_test_path)

讓我們看看一些數(shù)據(jù)集的統(tǒng)計(jì)數(shù)據(jù),例如訓(xùn)練系列的數(shù)量(如前所述,每個(gè)系列之間有 50% 的重疊),測(cè)試系列的數(shù)量,每個(gè)系列的時(shí)間步數(shù),以及每個(gè)時(shí)間步輸入?yún)?shù):

training_data_count = len(X_train)
test_data_count = len(X_test)
n_steps = len(X_train[0])  
n_input = len(X_train[0][0])  
print("Number of training series: "+ trainingDataCount)
print("Number of test series: "+ testDataCount)
print("Number of timestep per series: "+ nSteps)
print("Number of input parameters per timestep: "+ nInput) 

以下是上述代碼的輸出:

>>>
Number of training series: 7352
Number of test series: 2947
Number of timestep per series: 128
Number of input parameters per timestep: 9

現(xiàn)在讓我們?yōu)橛?xùn)練定義一些核心參數(shù)定義。整個(gè)神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)可以通過(guò)枚舉這些參數(shù)和使用 LSTM 這一事實(shí)來(lái)概括:

n_hidden = 32 # Hidden layer num of features
n_classes = 6 # Total classes (should go up, or should go down)learning_rate = 0.0025
lambda_loss_amount = 0.0015
training_iters = training_data_count * 300 #Iterate 300 times 
batch_size = 1500
display_iter = 30000 # to show test set accuracy during training

我們已經(jīng)定義了所有核心參數(shù)和網(wǎng)絡(luò)參數(shù)。這些是隨機(jī)選擇。我沒(méi)有進(jìn)行超參數(shù)調(diào)整,但仍然運(yùn)行良好。因此,我建議使用網(wǎng)格搜索技術(shù)調(diào)整這些超參數(shù)。有許多在線資料可供使用。

然而,在構(gòu)建 LSTM 網(wǎng)絡(luò)并開(kāi)始訓(xùn)練之前,讓我們打印一些調(diào)試信息,以確保執(zhí)行不會(huì)中途停止:

print("Some useful info to get an insight on dataset's shape and normalization:")
print("(X shape, y shape, every X's mean, every X's standard deviation)")
print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))
print("The dataset is therefore properly normalized, as expected, but not yet one-hot encoded.")

以下是上述代碼的輸出:

>>>
Some useful info to get an insight on dataset's shape and normalization:
(X shape, y shape, every X's mean, every X's standard deviation)
(2947, 128, 9) (2947, 1) 0.0991399 0.395671

數(shù)據(jù)集是 ,因此按預(yù)期正確標(biāo)準(zhǔn)化,但尚未進(jìn)行單熱編碼。

現(xiàn)在訓(xùn)練數(shù)據(jù)集處于校正和標(biāo)準(zhǔn)化順序,現(xiàn)在是構(gòu)建 LSTM 網(wǎng)絡(luò)的時(shí)候了。以下函數(shù)從給定參數(shù)返回 TensorFlow LSTM 網(wǎng)絡(luò)。此外,兩個(gè) LSTM 單元堆疊在一起,這增加了神經(jīng)網(wǎng)絡(luò)的深度:

def LSTM_RNN(_X, _weights, _biases):_X = tf.transpose(_X, [1,0,2])# permute n_steps & batch_size_X = tf.reshape(_X, [-1, n_input]) _X = tf.nn.relu(tf.matmul(_X, _weights['hidden']) + _biases['hidden'])_X = tf.split(_X, n_steps, 0) lstm_cell_1 = tf.nn.rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True)lstm_cell_2 = tf.nn.rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True)lstm_cells = tf.nn.rnn_cell.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32)lstm_last_output = outputs[-1]return tf.matmul(lstm_last_output, _weights['out']) + _biases['out']

如果我們仔細(xì)查看前面的代碼片段,我們可以看到我們得到了“多對(duì)一”樣式分類器的最后一步輸出特征?,F(xiàn)在,問(wèn)題是什么是多對(duì)一 RNN 分類器?好吧,類似于圖 5,我們接受特征向量的時(shí)間序列(每個(gè)時(shí)間步長(zhǎng)一個(gè)向量)并將它們轉(zhuǎn)換為輸出中的概率向量以進(jìn)行分類。

現(xiàn)在我們已經(jīng)能夠構(gòu)建我們的 LSTM 網(wǎng)絡(luò),我們需要將訓(xùn)練數(shù)據(jù)集準(zhǔn)備成批量。以下函數(shù)從(X|y)_train數(shù)據(jù)中獲取batch_size數(shù)據(jù)量:

def extract_batch_size(_train, step, batch_size):shape = list(_train.shape)shape[0] = batch_sizebatch_s = np.empty(shape)for i in range(batch_size):index = ((step-1)*batch_size + i) % len(_train)batch_s[i] = _train[index]return batch_s

之后,我們需要將數(shù)字索引的輸出標(biāo)簽編碼為二元類別。然后我們用batch_size執(zhí)行訓(xùn)練步驟。例如,[[5], [0], [3]]需要轉(zhuǎn)換為類似于[[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]]的形狀。好吧,我們可以用單熱編碼來(lái)做到這一點(diǎn)。以下方法執(zhí)行完全相同的轉(zhuǎn)換:

def one_hot(y_):y_ = y_.reshape(len(y_))n_values = int(np.max(y_)) + 1return np.eye(n_values)[np.array(y_, dtype=np.int32)]

優(yōu)秀的!我們的數(shù)據(jù)集準(zhǔn)備就緒,因此我們可以開(kāi)始構(gòu)建網(wǎng)絡(luò)。首先,我們?yōu)檩斎牒蜆?biāo)簽創(chuàng)建兩個(gè)單獨(dú)的占位符:

x = tf.placeholder(tf.float32, [None, n_steps, n_input])
y = tf.placeholder(tf.float32, [None, n_classes])

然后我們創(chuàng)建所需的權(quán)重向量:

weights = {'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), 'out': tf.Variable(tf.random_normal([n_hidden, n_classes], mean=1.0))
}

然后我們創(chuàng)建所需的偏向量:

biases = {'hidden': tf.Variable(tf.random_normal([n_hidden])),'out': tf.Variable(tf.random_normal([n_classes]))
}

然后我們通過(guò)傳遞輸入張量,權(quán)重向量和偏置向量來(lái)構(gòu)建模型,如下所示:

pred = LSTM_RNN(x, weights, biases)

此外,我們還需要計(jì)算cost操作,正則化,優(yōu)化器和評(píng)估。我們使用 L2 損失進(jìn)行正則化,這可以防止這種過(guò)度殺傷神經(jīng)網(wǎng)絡(luò)過(guò)度適應(yīng)訓(xùn)練中的問(wèn)題:

l2 = lambda_loss_amount * sum(tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables())cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=pred)) + l2optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
Great! So far, everything has been fine. Now we are ready to train the neural network. First, we create some lists to hold some training's performance:
test_losses = []
test_accuracies = []
train_losses = []
train_accuracies = []

然后我們創(chuàng)建一個(gè) TensorFlow 會(huì)話,啟動(dòng)圖并初始化全局變量:

sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=False))
init = tf.global_variables_initializer()
sess.run(init)

然后我們?cè)诿總€(gè)循環(huán)中以batch_size數(shù)量的示例數(shù)據(jù)執(zhí)行訓(xùn)練步驟。我們首先使用批量數(shù)據(jù)進(jìn)行訓(xùn)練,然后我們僅在幾個(gè)步驟評(píng)估網(wǎng)絡(luò)以加快訓(xùn)練速度。另外,我們?cè)u(píng)估測(cè)試集(這里沒(méi)有學(xué)習(xí),只是診斷評(píng)估)。最后,我們打印結(jié)果:

step = 1
while step * batch_size <= training_iters:batch_xs =  extract_batch_size(X_train, step, batch_size)batch_ys = one_hot(extract_batch_size(y_train, step, batch_size))_, loss, acc = sess.run([optimizer, cost, accuracy],feed_dict={x: batch_xs,y: batch_ys})train_losses.append(loss)train_accuracies.append(acc)if (step*batch_size % display_iter == 0) or (step == 1) or (step * batch_size > training_iters):print("Training iter #" + str(step*batch_size) + \":   Batch Loss = " + "{:.6f}".format(loss) + \", Accuracy = {}".format(acc))loss, acc = sess.run([cost, accuracy], feed_dict={x: X_test,y: one_hot(y_test)})test_losses.append(loss)test_accuracies.append(acc)print("PERFORMANCE ON TEST SET: " + \"Batch Loss = {}".format(loss) + \", Accuracy = {}".format(acc))step += 1
print("Optimization Finished!")
one_hot_predictions, accuracy, final_loss = sess.run([pred, accuracy, cost],feed_dict={x: X_test,y: one_hot(y_test)})
test_losses.append(final_loss)
test_accuracies.append(accuracy)print("FINAL RESULT: " + \"Batch Loss = {}".format(final_loss) + \", Accuracy = {}".format(accuracy))

以下是上述代碼的輸出:

>>>
Training iter #1500:   Batch Loss = 3.266330, Accuracy = 0.15733332931995392
PERFORMANCE ON TEST SET: Batch Loss = 2.6498606204986572, Accuracy = 0.15473362803459167
Training iter #30000:   Batch Loss = 1.538126, Accuracy = 0.6380000114440918
…PERFORMANCE ON TEST SET: Batch Loss = 0.5507552623748779, Accuracy = 0.8924329876899719
Optimization Finished!
FINAL RESULT: Batch Loss = 0.6077192425727844, Accuracy = 0.8686800003051758

做得好! 訓(xùn)練進(jìn)展順利。但是,視覺(jué)概述會(huì)更有用:

indep_train_axis = np.array(range(batch_size, (len(train_losses)+1)*batch_size, batch_size))
plt.plot(indep_train_axis, np.array(train_losses),     "b--", label="Train losses")
plt.plot(indep_train_axis, np.array(train_accuracies), "g--", label="Train accuracies")
indep_test_axis = np.append(np.array(range(batch_size, len(test_losses)*display_iter, display_iter)[:-1]),[training_iters])
plt.plot(indep_test_axis, np.array(test_losses),     "b-", label="Test losses")
plt.plot(indep_test_axis, np.array(test_accuracies), "g-", label="Test accuracies")
plt.title("Training session's progress over iterations")
plt.legend(loc='upper right', shadow=True)
plt.ylabel('Training Progress (Loss or Accuracy values)')
plt.xlabel('Training iteration')
plt.show()

以下是上述代碼的輸出:

>>>

Implementing an LSTM model for HAR

圖 26:迭代時(shí)的 LSTM 訓(xùn)練過(guò)程

我們需要計(jì)算其他表現(xiàn)指標(biāo),例如accuracyprecisionrecallf1度量:

predictions = one_hot_predictions.argmax(1)
print("Testing Accuracy: {}%".format(100*accuracy))
print("")
print("Precision: {}%".format(100*metrics.precision_score(y_test, predictions, average="weighted")))
print("Recall: {}%".format(100*metrics.recall_score(y_test, predictions, average="weighted")))
print("f1_score: {}%".format(100*metrics.f1_score(y_test, predictions, average="weighted")))

以下是上述代碼的輸出:

>>>
Testing Accuracy: 89.51476216316223%
Precision: 89.65053428376297%
Recall: 89.51476077366813%
f1_score: 89.48593061935716%

由于我們正在接近的問(wèn)題是多類分類,因此繪制混淆矩陣是有意義的:

print("")
print ("Showing Confusion Matrix")cm = metrics.confusion_matrix(y_test, predictions)
df_cm = pd.DataFrame(cm, LABELS, LABELS)
plt.figure(figsize = (16,8))
plt.ylabel('True label')
plt.xlabel('Predicted label')
sn.heatmap(df_cm, annot=True, annot_kws={"size": 14}, fmt='g', linewidths=.5)
plt.show()

以下是上述代碼的輸出:

>>>

Implementing an LSTM model for HAR

圖 27:多類混淆矩陣(預(yù)測(cè)與實(shí)際)

在混淆矩陣中,訓(xùn)練和測(cè)試數(shù)據(jù)不是在類之間平均分配,因此正常情況下,超過(guò)六分之一的數(shù)據(jù)在最后一類中被正確分類。話雖如此,我們已經(jīng)設(shè)法達(dá)到約 87% 的預(yù)測(cè)準(zhǔn)確率。我們很快就會(huì)看到更多分析。它可能更高,但是訓(xùn)練是在 CPU 上進(jìn)行的,因此它的精度很低,當(dāng)然需要很長(zhǎng)時(shí)間。因此,我建議你在 GPU 上訓(xùn)練,以獲得更好的結(jié)果。此外,調(diào)整超參數(shù)可能是一個(gè)不錯(cuò)的選擇。

總結(jié)

LSTM 網(wǎng)絡(luò)配備了特殊的隱藏單元,稱為存儲(chǔ)單元,其目的是長(zhǎng)時(shí)間記住先前的輸入。這些單元在每個(gè)時(shí)刻采用先前狀態(tài)和網(wǎng)絡(luò)的當(dāng)前輸入作為輸入。通過(guò)將它們與內(nèi)存的當(dāng)前內(nèi)容相結(jié)合,并通過(guò)其他單元的門控機(jī)制決定保留什么以及從內(nèi)存中刪除什么,LSTM 已被證明是非常有用的并且是學(xué)習(xí)長(zhǎng)期依賴性的有效方式。

在本章中,我們討論了 RNN。我們看到了如何使用具有高時(shí)間依賴性的數(shù)據(jù)進(jìn)行預(yù)測(cè)。我們看到了如何開(kāi)發(fā)幾種真實(shí)的預(yù)測(cè)模型,使用 RNN 和不同的架構(gòu)變體使預(yù)測(cè)分析更容易。我們從 RNN 的理論背景開(kāi)始。

然后我們看了幾個(gè)例子,展示了一種實(shí)現(xiàn)圖像分類預(yù)測(cè)模型,電影和產(chǎn)品情感分析以及 NLP 垃圾郵件預(yù)測(cè)的系統(tǒng)方法。然后我們看到了如何開(kāi)發(fā)時(shí)間序列數(shù)據(jù)的預(yù)測(cè)模型。最后,我們看到了 RNN 用于人類活動(dòng)識(shí)別的更高級(jí)應(yīng)用,我們觀察到分類準(zhǔn)確率約為 87%。

DNN 以統(tǒng)一的方式構(gòu)造,使得在網(wǎng)絡(luò)的每一層,數(shù)千個(gè)相同的人工神經(jīng)元執(zhí)行相同的計(jì)算。因此,DNN 的架構(gòu)非常適合 GPU 可以有效執(zhí)行的計(jì)算類型。 GPU 具有優(yōu)于 CPU 的額外優(yōu)勢(shì);這些包括具有更多計(jì)算單元并具有更高的帶寬用于存儲(chǔ)器檢索。

此外,在許多需要大量計(jì)算工作的深度學(xué)習(xí)應(yīng)用中,可以利用 GPU 的圖形特定功能來(lái)進(jìn)一步加速計(jì)算。在下一章中,我們將看到如何使訓(xùn)練更快,更準(zhǔn)確,甚至在節(jié)點(diǎn)之間分配。

七、TensorFlow GPU 配置

要將 TensorFlow 與 NVIDIA GPU 配合使用,第一步是安裝 CUDA 工具包。

注意

要了解更多信息,請(qǐng)?jiān)L問(wèn)此鏈接。

安裝 CUDA 工具包后,必須從此鏈接下載適用于 Linux 的 cuDNN v5.1 庫(kù)。

cuDNN 是一個(gè)有助于加速深度學(xué)習(xí)框架的庫(kù),例如 TensorFlow 和 Theano。以下是 NVIDIA 網(wǎng)站的簡(jiǎn)要說(shuō)明:

NVIDIACUDA?深度神經(jīng)網(wǎng)絡(luò)庫(kù)(cuDNN)是用于深度神經(jīng)網(wǎng)絡(luò)的 GPU 加速原語(yǔ)庫(kù).cuDNN 為標(biāo)準(zhǔn)例程提供高度調(diào)整的實(shí)現(xiàn),例如前向和后向卷積,池化,正則化和激活層。cuDNN 是 NVIDIA 深度學(xué)習(xí) SDK 的一部分。

在安裝之前,您需要在 NVIDIA 的加速計(jì)算開(kāi)發(fā)人員計(jì)劃中注冊(cè)。注冊(cè)后,登錄并將 cuDNN 5.1 下載到本地計(jì)算機(jī)。

下載完成后,解壓縮文件并將其復(fù)制到 CUDA 工具包目錄中(我們假設(shè)目錄為/usr/local/cuda/):

$ sudo tar -xvf cudnn-8.0-linux-x64-v5.1-rc.tgz -C /usr/local

更新 TensorFlow

我們假設(shè)您將使用 TensorFlow 來(lái)構(gòu)建您的深度神經(jīng)網(wǎng)絡(luò)模型。只需通過(guò) PIP 用upgrade標(biāo)志更新 TensorFlow。

我們假設(shè)您當(dāng)前正在使用 TensorFlow 0.11:

pip install — upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.10.0rc0-cp27-none-linux_x86_64.whl

現(xiàn)在,您應(yīng)該擁有使用 GPU 運(yùn)行模型所需的一切。

GPU 表示

在 TensorFlow 中, 支持的設(shè)備表示為字符串:

  • "/cpu:0":您機(jī)器的 CPU
  • "/gpu:0":機(jī)器的 GPU,如果你有的話
  • "/gpu:1":你機(jī)器的第二個(gè) GPU,依此類推

當(dāng)將操作分配給 GPU 設(shè)備時(shí), 執(zhí)行流程給予優(yōu)先級(jí)。

GPU 使用

要在 TensorFlow 程序中使用 GPU,只需鍵入以下內(nèi)容:

with tf.device("/gpu:0"):

然后你需要進(jìn)行設(shè)置操作。這行代碼將創(chuàng)建一個(gè)新的上下文管理器,告訴 TensorFlow 在 GPU 上執(zhí)行這些操作。

讓我們考慮以下示例,其中我們要執(zhí)行以下兩個(gè)大矩陣的和:A^n + B^n

定義基本導(dǎo)入:

import numpy as np
import tensorflow as tf
import datetime

我們可以配置 TensorFlow 程序,以找出您的操作和張量分配給哪些設(shè)備。為此,我們將創(chuàng)建一個(gè)會(huì)話,并將以下log_device_placement參數(shù)設(shè)置為True

log_device_placement = True

然后我們?cè)O(shè)置n參數(shù),這是要執(zhí)行的乘法次數(shù):

n=10

然后我們構(gòu)建兩個(gè)隨機(jī)大矩陣。我們使用 NumPy rand函數(shù)來(lái)執(zhí)行此操作:

A = np.random.rand(10000, 10000).astype('float32')
B = np.random.rand(10000, 10000).astype('float32')

AB各自的大小為10000x10000。

以下數(shù)組將用于存儲(chǔ)結(jié)果:

c1 = []
c2 = []

接下來(lái),我們定義將由 GPU 執(zhí)行的內(nèi)核矩陣乘法函數(shù):

def matpow(M, n):if n == 1:return Melse:return tf.matmul(M, matpow(M, n-1))

正如我們之前解釋的 ,我們必須配置 GPU 和 GPU 以執(zhí)行以下操作:

GPU 將計(jì)算A^nB^n操作并將結(jié)果存儲(chǔ)在c1中:

with tf.device('/gpu:0'):a = tf.placeholder(tf.float32, [10000, 10000])b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))c1.append(matpow(b, n))

c1A^n + B^n)中所有元素的添加由 CPU 執(zhí)行,因此我們定義以下內(nèi)容:

with tf.device('/cpu:0'):sum = tf.add_n(c1) 

datetime類允許我們得到計(jì)算時(shí)間:

t1_1 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto\(log_device_placement=log_device_placement)) as sess:sess.run(sum, {a:A, b:B})
t2_1 = datetime.datetime.now()

然后顯示計(jì)算時(shí)間:

print("GPU computation time: " + str(t2_1-t1_1))

在我的筆記本電腦上,使用 GeForce 840M 顯卡,結(jié)果如下:

GPU computation time: 0:00:13.816644

GPU 內(nèi)存管理

在一些情況下,希望該過(guò)程僅分配可用內(nèi)存的子集,或者僅增加該過(guò)程所需的內(nèi)存使用量。 TensorFlow 在會(huì)話中提供兩個(gè)配置選項(xiàng)來(lái)控制它。

第一個(gè)是allow_growth選項(xiàng),它嘗試根據(jù)運(yùn)行時(shí)分配僅分配盡可能多的 GPU 內(nèi)存:它開(kāi)始分配非常少的內(nèi)存,并且隨著會(huì)話運(yùn)行并需要更多 GPU 內(nèi)存,我們擴(kuò)展 GPU 內(nèi)存量 TensorFlow 流程需要。

請(qǐng)注意, 我們不釋放內(nèi)存,因?yàn)檫@可能導(dǎo)致更糟糕的內(nèi)存碎片。要打開(kāi)此選項(xiàng),請(qǐng)?jiān)?code>ConfigProto中設(shè)置選項(xiàng),如下所示:

config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config, ...)

第二種方法是per_process_gpu_memory_fraction選項(xiàng),它確定應(yīng)分配每個(gè)可見(jiàn) GPU 的總內(nèi)存量的分?jǐn)?shù)。例如,您可以告訴 TensorFlow 僅分配每個(gè) GPU 總內(nèi)存的 40%,如下所示:

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config, ...)

如果要真正限制 TensorFlow 進(jìn)程可用的 GPU 內(nèi)存量,這非常有用。

在多 GPU 系統(tǒng)上分配單個(gè) GPU

如果系統(tǒng)中有多個(gè) GPU,則默認(rèn)情況下將選擇 ID 最低的 GPU。如果您想在不同的 GPU 上運(yùn)行會(huì)話,則需要明確指定首選項(xiàng)。

例如,我們可以嘗試更改上一代碼中的 GPU 分配:

with tf.device('/gpu:1'):a = tf.placeholder(tf.float32, [10000, 10000])b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))c1.append(matpow(b, n))

這樣,我們告訴gpu1執(zhí)行內(nèi)核函數(shù)。如果我們指定的設(shè)備不??存在(如我的情況),您將獲得InvalidArgumentError

InvalidArgumentError (see above for traceback): Cannot assign a device to node 'Placeholder_1': Could not satisfy explicit device specification '/device:GPU:1' because no devices matching that specification are registered in this process; available devices: /job:localhost/replica:0/task:0/cpu:0[[Node: Placeholder_1 = Placeholder[dtype=DT_FLOAT, shape=[100,100], _device="/device:GPU:1"]()]]

如果您希望 TensorFlow 自動(dòng)選擇現(xiàn)有且受支持的設(shè)備來(lái)運(yùn)行操作(如果指定的設(shè)備不??存在),則可以在創(chuàng)建會(huì)話時(shí)在配置選項(xiàng)中將allow_soft_placement設(shè)置為True

我們?cè)俅螢橐韵鹿?jié)點(diǎn)設(shè)置'/gpu:1'

with tf.device('/gpu:1'):a = tf.placeholder(tf.float32, [10000, 10000])b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))c1.append(matpow(b, n))

然后我們構(gòu)建一個(gè)Session,并將以下allow_soft_placement參數(shù)設(shè)置為True

with tf.Session(config=tf.ConfigProto\(allow_soft_placement=True,\log_device_placement=log_device_placement))\as sess:

這樣,當(dāng)我們運(yùn)行會(huì)話時(shí),不會(huì)顯示InvalidArgumentError。在這種情況下,我們會(huì)得到一個(gè)正確的結(jié)果,但有一點(diǎn)延遲:

GPU computation time: 0:00:15.006644

具有軟放置的 GPU 的源代碼

這是完整源代碼,僅為了清楚起見(jiàn):

import numpy as np
import tensorflow as tf
import datetimelog_device_placement = True
n = 10A = np.random.rand(10000, 10000).astype('float32')
B = np.random.rand(10000, 10000).astype('float32')c1 = []
c2 = []def matpow(M, n):if n == 1: return Melse:return tf.matmul(M, matpow(M, n-1))with tf.device('/gpu:0'):a = tf.placeholder(tf.float32, [10000, 10000])b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))c1.append(matpow(b, n))with tf.device('/cpu:0'):sum = tf.add_n(c1) t1_1 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto\(allow_soft_placement=True,\log_device_placement=log_device_placement))\as sess:sess.run(sum, {a:A, b:B})
t2_1 = datetime.datetime.now()

使用多個(gè) GPU

如果您想在多個(gè) GPU 上運(yùn)行 TensorFlow,您可以通過(guò)為 GPU 分配特定的代碼塊來(lái)構(gòu)建模型。例如,如果我們有兩個(gè) GPU,我們可以按如下方式拆分前面的代碼,將第一個(gè)矩陣計(jì)算分配給第一個(gè) GPU:

with tf.device('/gpu:0'):a = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))

第二個(gè)矩陣計(jì)算分配給第二個(gè) GPU:

with tf.device('/gpu:1'):b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(b, n))

CPU 將管理結(jié)果。另請(qǐng)注意,我們使用共享c1數(shù)組來(lái)收集它們:

with tf.device('/cpu:0'):sum = tf.add_n(c1)

在下面的代碼片段中,我們提供了兩個(gè) GPU 管理的具體示例:

import numpy as np
import tensorflow as tf
import datetimelog_device_placement = True
n = 10A = np.random.rand(10000, 10000).astype('float32')
B = np.random.rand(10000, 10000).astype('float32')c1 = []def matpow(M, n):if n == 1:  return Melse:return tf.matmul(M, matpow(M, n-1))#FIRST GPU
with tf.device('/gpu:0'):a = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(a, n))#SECOND GPU
with tf.device('/gpu:1'):b = tf.placeholder(tf.float32, [10000, 10000])c1.append(matpow(b, n))with tf.device('/cpu:0'):sum = tf.add_n(c1) t1_1 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto\(allow_soft_placement=True,\log_device_placement=log_device_placement))\as sess:sess.run(sum, {a:A, b:B})
t2_1 = datetime.datetime.now()

分布式計(jì)算

DL 模型必須接受大量數(shù)據(jù)的訓(xùn)練,以提高其表現(xiàn)。但是,訓(xùn)練具有數(shù)百萬(wàn)參數(shù)的深度網(wǎng)絡(luò)可能需要數(shù)天甚至數(shù)周。在大規(guī)模分布式深度網(wǎng)絡(luò)中,Dean 等人。提出了兩種范例,即模型并行性和數(shù)據(jù)并行性,它們?cè)试S我們?cè)诙鄠€(gè)物理機(jī)器上訓(xùn)練和服務(wù)網(wǎng)絡(luò)模型。在下一節(jié)中,我們引入了這些范例,重點(diǎn)關(guān)注分布式 TensorFlow 功能。

模型并行

模型并行性為每個(gè)處理器提供相同的數(shù)據(jù),但對(duì)其應(yīng)用不同的模型。如果網(wǎng)絡(luò)模型太大而無(wú)法放入一臺(tái)機(jī)器的內(nèi)存中,則可以將模型的不同部分分配給不同的機(jī)器??赡艿哪P筒⑿蟹椒ㄊ窃跈C(jī)器(節(jié)點(diǎn) 1)上具有第一層,在第二機(jī)器(節(jié)點(diǎn) 2)上具有第二層,等等。有時(shí)這不是最佳方法,因?yàn)樽詈笠粚颖仨毜却谇斑M(jìn)步驟期間完成第一層的計(jì)算,并且第一層必須在反向傳播步驟期間等待最深層。只有模型可并行化(例如 GoogleNet)才能在不同的機(jī)器上實(shí)現(xiàn),而不會(huì)遇到這樣的瓶頸:

Model parallelism

圖 3:在模型并行性中,每個(gè)節(jié)點(diǎn)計(jì)算網(wǎng)絡(luò)的不同部分

大約 20 年前, 訓(xùn)練神經(jīng)網(wǎng)絡(luò)的人可能是術(shù)語(yǔ)模型并行性的創(chuàng)始人,因?yàn)樗麄冇胁煌纳窠?jīng)網(wǎng)絡(luò)模型來(lái)訓(xùn)練和測(cè)試,并且網(wǎng)絡(luò)中的多個(gè)層可以用相同的數(shù)據(jù)。

數(shù)據(jù)并行

數(shù)據(jù)并行性表示將單個(gè)指令應(yīng)用于多個(gè)數(shù)據(jù)項(xiàng)。它是 SIMD(單指令,多數(shù)據(jù))計(jì)算機(jī)架構(gòu)的理想工作負(fù)載,是電子數(shù)字計(jì)算機(jī)上最古老,最簡(jiǎn)單的并行處理形式。

在這種方法中,網(wǎng)絡(luò)模型適合于一臺(tái)機(jī)器,稱為參數(shù)服務(wù)器,而大多數(shù)計(jì)算工作由多臺(tái)機(jī)器完成,稱為工作器:

  • 參數(shù)服務(wù)器:這是一個(gè) CPU,您可以在其中存儲(chǔ)工作器所需的變量。就我而言,這是我定義網(wǎng)絡(luò)所需的權(quán)重變量的地方。
  • 工作器:這是我們完成大部分計(jì)算密集型工作的地方。

每個(gè)工作器負(fù)責(zé)讀取,計(jì)算和更新模型參數(shù),并將它們發(fā)送到參數(shù)服務(wù)器:

  • 在正向傳播中,工作者從參數(shù)服務(wù)器獲取變量,在我們的工作者上對(duì)它們執(zhí)行某些操作。

  • 在向后傳遞中,工作器將當(dāng)前狀態(tài)發(fā)送回參數(shù)服務(wù)器,參數(shù)服務(wù)器執(zhí)行更新操作,并為我們提供新的權(quán)重以進(jìn)行嘗試:

    Data parallelism

    圖 4:在數(shù)據(jù)并行模型中,每個(gè)節(jié)點(diǎn)計(jì)算所有參數(shù)

數(shù)據(jù)并行可能有兩個(gè)主要的選項(xiàng):

  • 同步訓(xùn)練:所有工作器同時(shí)讀取參數(shù),計(jì)算訓(xùn)練操作,并等待所有其他人完成。然后將梯度平均,并將單個(gè)更新發(fā)送到參數(shù)服務(wù)器。因此,在任何時(shí)間點(diǎn),工作器都將意識(shí)到圖參數(shù)的相同值。
  • 異步訓(xùn)練:工作器將異步讀取參數(shù)服務(wù)器,計(jì)算訓(xùn)練操作,并發(fā)送異步更新。在任何時(shí)間點(diǎn),兩個(gè)不同的工作器可能會(huì)意識(shí)到圖參數(shù)的不同值。

分布式 TensorFlow 配置

在本節(jié)中,我們將探索 TensorFlow 中的計(jì)算可以分布的機(jī)制。運(yùn)行分布式 TensorFlow 的第一步是使用tf.train.ClusterSpec指定集群的架構(gòu):

import tensorflow as tfcluster = tf.train.ClusterSpec({"ps": ["localhost:2222"],\"worker": ["localhost:2223",\"localhost:2224"]})

節(jié)點(diǎn)通常分為兩個(gè)作業(yè):主機(jī)變量的參數(shù)服務(wù)器(ps)和執(zhí)行大量計(jì)算的工作器。在上面的代碼中,我們有一個(gè)參數(shù)服務(wù)器和兩個(gè)工作器,以及每個(gè)節(jié)點(diǎn)的 IP 地址和端口。

然后我們必須為之前定義的每個(gè)參數(shù)服務(wù)器和工作器構(gòu)建一個(gè)tf.train.Server

ps = tf.train.Server(cluster, job_name="ps", task_index=0)worker0 = tf.train.Server(cluster,\job_name="worker", task_index=0)
worker1 = tf.train.Server(cluster,\job_name="worker", task_index=1)

tf.train.Server對(duì)象包含一組本地設(shè)備,一組與tf.train.ClusterSpec中其他任務(wù)的連接,以及一個(gè)可以使用它們執(zhí)行分布式計(jì)算的tf.Session。創(chuàng)建它是為了允許設(shè)備之間的連接。

接下來(lái),我們使用以下命令將模型變量分配給工作器:

tf.device :with tf.device("/job:ps/task:0"):a = tf.constant(3.0, dtype=tf.float32)b = tf.constant(4.0)

將這些指令復(fù)制到名為main.py的文件中。

在兩個(gè)單獨(dú)的文件worker0.pyworker1.py中,我們必須定義工作器。在worker0.py中,將兩個(gè)變量ab相乘并打印出結(jié)果:

import tensorflow as tf
from main import *with tf.Session(worker0.target) as sess:init = tf.global_variables_initializer()add_node = tf.multiply(a,b)sess.run(init)print(sess.run(add_node))

worker1.py中,首先更改a的值,然后將兩個(gè)變量ab相乘:

import tensorflow as tf
from main import *with tf.Session(worker1.target) as sess:init = tf.global_variables_initializer()a = tf.constant(10.0, dtype=tf.float32)add_node = tf.multiply(a,b)sess.run(init)a = add_nodeprint(sess.run(add_node))

要執(zhí)行此示例,首先從命令提示符運(yùn)行main.py文件。

你應(yīng)該得到這樣的結(jié)果:

>python main.pyFound device 0 with properties:
name: GeForce 840M
major: 5 minor: 0 memoryClockRate (GHz) 1.124
pciBusID 0000:08:00.0
Total memory: 2.00GiB
Free memory: 1.66GiBStarted server with target: grpc://localhost:2222

然后我們可以運(yùn)行工作器:

> python worker0.pyFound device 0 with properties:
name: GeForce 840M
major: 5 minor: 0 memoryClockRate (GHz) 1.124
pciBusID 0000:08:00.0
Total memory: 2.00GiB
Free memory: 1.66GiBStart master session 83740f48d039c97d with config:12.0
> python worker1.pyFound device 0 with properties:
name: GeForce 840M
major: 5 minor: 0 memoryClockRate (GHz) 1.124
pciBusID 0000:08:00.0
Total memory: 2.00GiB
Free memory: 1.66GiBStart master session 3465f63a4d9feb85 with config:40.0

總結(jié)

在本章中,我們快速了解了與優(yōu)化 DNN 計(jì)算相關(guān)的兩個(gè)基本主題。

第一個(gè)主題解釋了如何使用 GPU 和 TensorFlow 來(lái)實(shí)現(xiàn) DNN。它們以非常統(tǒng)一的方式構(gòu)造,使得在網(wǎng)絡(luò)的每一層,數(shù)千個(gè)相同的人工神經(jīng)元執(zhí)行相同的計(jì)算。因此,DNN 的架構(gòu)非常適合 GPU 可以有效執(zhí)行的計(jì)算類型。

第二個(gè)主題介紹了分布式計(jì)算。這最初用于執(zhí)行非常復(fù)雜的計(jì)算,這些計(jì)算無(wú)法由單個(gè)機(jī)器完成。同樣,在面對(duì)如此大的挑戰(zhàn)時(shí),通過(guò)在不同節(jié)點(diǎn)之間拆分此任務(wù)來(lái)快速分析大量數(shù)據(jù)似乎是最佳策略。

同時(shí),可以使用分布式計(jì)算來(lái)利用 DL 問(wèn)題。 DL 計(jì)算可以分為多個(gè)活動(dòng)(任務(wù));他們每個(gè)人都將獲得一小部分?jǐn)?shù)據(jù),并返回一個(gè)結(jié)果,該結(jié)果必須與其他活動(dòng)提供的結(jié)果重新組合?;蛘?#xff0c;在大多數(shù)復(fù)雜情況下,可以為每臺(tái)機(jī)器分配不同的計(jì)算算法。

最后,在最后一個(gè)例子中,我們展示了如何分配 TensorFlow 中的計(jì)算。

八、TFLearn

TFLearn 是一個(gè)庫(kù),它使用漂亮且熟悉的 scikit-learn API 包裝了許多新的 TensorFlow API。

TensorFlow 是關(guān)于構(gòu)建和執(zhí)行圖的全部?jī)?nèi)容。這是一個(gè)非常強(qiáng)大的概念,但從一開(kāi)始就很麻煩。

在 TFLearn 的引擎蓋下,我們只使用了三個(gè)部分:

  • 層:一組高級(jí) TensorFlow 函數(shù),允許我們輕松構(gòu)建復(fù)雜的圖,從完全連接的層,卷積和批量規(guī)范到損失和優(yōu)化。
  • graph_actions:一組工具,用于對(duì) TensorFlow 圖進(jìn)行訓(xùn)練,評(píng)估和運(yùn)行推理。
  • 估計(jì)器:將所有內(nèi)容打包成一個(gè)遵循 scikit-learn 接口的類,并提供了一種輕松構(gòu)建和訓(xùn)練自定義 TensorFlow 模型的方法。

安裝

要安裝 TFLearn, 最簡(jiǎn)單的方法是運(yùn)行以下命令:

pip install git+https://github.com/tflearn/tflearn.git

對(duì)于最新的穩(wěn)定版本,請(qǐng)使用以下命令:

pip install tflearn

否則,您也可以通過(guò)運(yùn)行以下命令(從源文件夾)從源安裝它:

python setup.py install

泰坦尼克號(hào)生存預(yù)測(cè)器

在本教程中,我們將學(xué)習(xí)使用 TFLearn 和 TensorFlow,使用他們的個(gè)人信息(如性別和年齡)模擬泰坦尼克號(hào)乘客的生存機(jī)會(huì)。為了解決這個(gè)經(jīng)典的 ML 任務(wù),我們將構(gòu)建一個(gè) DNN 分類器。

我們來(lái)看看數(shù)據(jù)集(TFLearn 將自動(dòng)為您下載)。

對(duì)于每位乘客,提供以下信息:

survived       Survived (0 = No; 1 = Yes)
pclass            Passenger Class (1 = st; 2 = nd; 3 = rd)
name             Name
sex                  Sex
age                 Age
sibsp              Number of Siblings/Spouses Aboard
parch             Number of Parents/Children Aboard
ticket             Ticket Number
fare               Passenger Fare

以下是數(shù)據(jù)集中的一些示例:

sibspparchticketfare
11Aubart, Mme. Leontine Paulinefemale
02Bowenur, Mr. Solomonmale
13Baclini, Miss. Marie Catherinefemale
03Youseff, Mr. Geriousmale

我們的任務(wù)有兩個(gè)類:沒(méi)有幸存(class = 0)和幸存(class = 1)。乘客數(shù)據(jù)有 8 個(gè)特征。泰坦尼克號(hào)數(shù)據(jù)集存儲(chǔ)在 CSV 文件中,因此我們可以使用TFLearn load_csv()函數(shù)將文件中的數(shù)據(jù)加載到 Python 列表中。我們指定target_column參數(shù)以指示我們的標(biāo)簽(幸存與否)位于第一列(ID:0)。這些函數(shù)將返回一個(gè)元組:(數(shù)據(jù),標(biāo)簽)。

讓我們從導(dǎo)入 NumPy 和 TFLearn 庫(kù)開(kāi)始:

import numpy as np
import tflearn as tfl

下載泰坦尼克號(hào)數(shù)據(jù)集:

from tflearn.datasets import titanic
titanic.download_dataset('titanic_dataset.csv')

加載 CSV 文件,并指出第一列代表labels

from tflearn.data_utils import load_csv
data, labels = load_csv('titanic_dataset.csv', target_column=0,categorical_labels=True, n_classes=2)

在準(zhǔn)備好在我們的 DNN 分類器中使用之前,數(shù)據(jù)需要一些預(yù)處理。我們必須刪除無(wú)法幫助我們進(jìn)行分析的列字段。我們丟棄名稱和票據(jù)字段,因?yàn)槲覀児烙?jì)乘客的姓名和票證與他們的幸存機(jī)會(huì)無(wú)關(guān):

def preprocess(data, columns_to_ignore):

預(yù)處理階段從降序 ID 和刪除列開(kāi)始:

    for id in sorted(columns_to_ignore, reverse=True):[r.pop(id) for r in data]for i in range(len(data)):

性別場(chǎng)轉(zhuǎn)換為浮動(dòng)(待操縱):

    data[i][1] = 1\. if data[i][1] == 'female' else 0.return np.array(data, dtype=np.float32)

如前所述,分析將忽略名稱和故障單字段:

to_ignore=[1, 6]

然后我們調(diào)用preprocess程序:

data = preprocess(data, to_ignore)

接下來(lái),我們指定輸入數(shù)據(jù)的形狀。輸入樣本總共有6特征,我們將分批量樣本以節(jié)省內(nèi)存,因此我們的數(shù)據(jù)輸入形狀為[None, 6]。None參數(shù)表示未知維度,因此我們可以更改批量中處理的樣本總數(shù):

net = tfl.input_data(shape=[None, 6])

最后,我們用這個(gè)簡(jiǎn)單的語(yǔ)句序列構(gòu)建了一個(gè) 3 層神經(jīng)網(wǎng)絡(luò):

net = tfl.fully_connected(net, 32)
net = tfl.fully_connected(net, 32)
net = tfl.fully_connected(net, 2, activation='softmax')
net = tfl.regression(net)

TFLearn 提供了一個(gè)模型包裝器DNN,它自動(dòng)執(zhí)行神經(jīng)網(wǎng)絡(luò)分類器任務(wù):

model = tfl.DNN(net)

我們將為批量大小為1610周期運(yùn)行它:

model.fit(data, labels, n_epoch=10, batch_size=16, show_metric=True)

當(dāng)我們運(yùn)行模型時(shí),我們應(yīng)該得到以下輸出:

Training samples: 1309
Validation samples: 0
--
Training Step: 82  | total loss: 0.64003
| Adam | epoch: 001 | loss: 0.64003 - acc: 0.6620 -- iter: 1309/1309
--
Training Step: 164  | total loss: 0.61915
| Adam | epoch: 002 | loss: 0.61915 - acc: 0.6614 -- iter: 1309/1309
--
Training Step: 246  | total loss: 0.56067
| Adam | epoch: 003 | loss: 0.56067 - acc: 0.7171 -- iter: 1309/1309
--
Training Step: 328  | total loss: 0.51807
| Adam | epoch: 004 | loss: 0.51807 - acc: 0.7799 -- iter: 1309/1309
--
Training Step: 410  | total loss: 0.47475
| Adam | epoch: 005 | loss: 0.47475 - acc: 0.7962 -- iter: 1309/1309
--
Training Step: 574  | total loss: 0.48988
| Adam | epoch: 007 | loss: 0.48988 - acc: 0.7891 -- iter: 1309/1309
--
Training Step: 656  | total loss: 0.55073
| Adam | epoch: 008 | loss: 0.55073 - acc: 0.7427 -- iter: 1309/1309
--
Training Step: 738  | total loss: 0.50242
| Adam | epoch: 009 | loss: 0.50242 - acc: 0.7854 -- iter: 1309/1309
--
Training Step: 820  | total loss: 0.41557
| Adam | epoch: 010 | loss: 0.41557 - acc: 0.8110 -- iter: 1309/1309
--

模型準(zhǔn)確率約為 81%,這意味著它可以預(yù)測(cè) 81% 乘客的正確結(jié)果(即乘客是否幸存)。

PrettyTensor

PrettyTensor 允許開(kāi)發(fā)人員包裝 TensorFlow 操作,以快速鏈接任意數(shù)量的層來(lái)定義神經(jīng)網(wǎng)絡(luò)。即將推出的是 PrettyTensor 功能的簡(jiǎn)單示例:我們將一個(gè)標(biāo)準(zhǔn)的 TensorFlow 對(duì)象包裝成一個(gè)與庫(kù)兼容的對(duì)象;然后我們通過(guò)三個(gè)完全連接的層提供它,我們最終輸出 softmax 分布:

pretty = tf.placeholder([None, 784], tf.float32)
softmax = (prettytensor.wrap(examples).fully_connected(256, tf.nn.relu).fully_connected(128, tf.sigmoid).fully_connected(64, tf.tanh).softmax(10))

PrettyTensor 安裝非常簡(jiǎn)單。您只需使用pip安裝程序:

sudo pip install prettytensor

鏈接層

PrettyTensor 具有三種操作模式,共享鏈?zhǔn)椒椒ǖ哪芰Α?/p>

正常模式

在正常模式下,每調(diào)用一次,就會(huì)創(chuàng)建一個(gè)新的 PrettyTensor。這樣可以輕松鏈接,您仍然可以多次使用任何特定對(duì)象。這樣可以輕松分支網(wǎng)絡(luò)。

順序模式

在順序模式下, 內(nèi)部變量 - 頭部 - 跟蹤最近的輸出張量,從而允許將調(diào)用鏈分解為多個(gè)語(yǔ)句。

這是一個(gè)簡(jiǎn)單的例子:

seq = pretty_tensor.wrap(input_data).sequential()
seq.flatten()
seq.fully_connected(200, activation_fn=tf.nn.relu)
seq.fully_connected(10, activation_fn=None)
result = seq.softmax(labels, name=softmax_name))

分支和連接

可以使用一流branchjoin方法構(gòu)建復(fù)雜網(wǎng)絡(luò):

  • branch創(chuàng)建一個(gè)單獨(dú)的 PrettyTensor 對(duì)象,該對(duì)象在調(diào)用時(shí)指向當(dāng)前頭部,這允許用戶定義一個(gè)單獨(dú)的塔,該塔以回歸目標(biāo)結(jié)束,以輸出結(jié)束或重新連接網(wǎng)絡(luò)。重新連接允許用戶定義復(fù)合層,如初始。
  • join用于連接多個(gè)輸入或重新連接復(fù)合層。

數(shù)字分類器

在這個(gè)例子中,我們將定義和訓(xùn)練一個(gè)兩層模型和一個(gè) LeNe??t 5 風(fēng)格的卷積模型:

import tensorflow as tf
import prettytensor as pt
from prettytensor.tutorial import data_utilstf.app.flags.DEFINE_string('save_path',\None, \'Where to save the model checkpoints.')FLAGS = tf.app.flags.FLAGSBATCH_SIZE = 50
EPOCH_SIZE = 60000
TEST_SIZE = 10000

由于我們將數(shù)據(jù)作為 NumPy 數(shù)組提供,因此我們需要在圖中創(chuàng)建占位符。這些然后必須使用進(jìn)料dict語(yǔ)句饋送:

image_placeholder = tf.placeholder\(tf.float32, [BATCH_SIZE, 28, 28, 1])labels_placeholder = tf.placeholder\(tf.float32, [BATCH_SIZE, 10])

接下來(lái),我們創(chuàng)建multilayer_fully_connected函數(shù)。前兩層是完全連接的(100神經(jīng)元),最后一層是softmax結(jié)果層。如您所見(jiàn),鏈接層是一個(gè)非常簡(jiǎn)單的操作:

def multilayer_fully_connected(images, labels):images = pt.wrap(images)with pt.defaults_scope\(activation_fn=tf.nn.relu,l2loss=0.00001):return (images.flatten().\fully_connected(100).\fully_connected(100).\softmax_classifier(10, labels))

現(xiàn)在我們將構(gòu)建一個(gè)多層卷積網(wǎng)絡(luò):該架構(gòu)類似于 LeNet5。請(qǐng)更改此設(shè)置,以便您可以嘗試其他架構(gòu):

def lenet5(images, labels):images = pt.wrap(images)with pt.defaults_scope\(activation_fn=tf.nn.relu, l2loss=0.00001):return (images.conv2d(5, 20).\max_pool(2, 2).\conv2d(5, 50).\max_pool(2, 2).\flatten().\fully_connected(500).\softmax_classifier(10, labels))

根據(jù)所選模型,我們可能有一個(gè) 2 層分類器(multilayer_fully_connected)或卷積分類器(lenet5):

def make_choice():var = int(input('(1) = multy layer model   (2) = LeNet5 '))print(var)if var == 1:result = multilayer_fully_connected\(image_placeholder,labels_placeholder)run_model(result)elif var == 2:result = lenet5\(image_placeholder,labels_placeholder)run_model(result) else:print ('incorrect input value')

最后,我們將為所選模型定義accuracy

def run_model(result):accuracy = result.softmax.evaluate_classifier\(labels_placeholder,phase=pt.Phase.test)

接下來(lái),我們構(gòu)建訓(xùn)練和測(cè)試集:

  train_images, train_labels = data_utils.mnist(training=True)test_images, test_labels = data_utils.mnist(training=False)

我們將使用梯度下降優(yōu)化程序并將其應(yīng)用于圖。pt.apply_optimizer函數(shù)增加了正則化損失并設(shè)置了一個(gè)步進(jìn)計(jì)數(shù)器:

  optimizer = tf.train.GradientDescentOptimizer(0.01)train_op = pt.apply_optimizer\(optimizer,losses=[result.loss])

我們可以在正在運(yùn)行的會(huì)話中設(shè)置save_path,以便每隔一段時(shí)間自動(dòng)保存進(jìn)度。否則,模型將在會(huì)話結(jié)束時(shí)丟失:

runner = pt.train.Runner(save_path=FLAGS.save_path)
with tf.Session():for epoch in range(0,10)

隨機(jī)展示訓(xùn)練數(shù)據(jù):

        train_images, train_labels = \data_utils.permute_data\((train_images, train_labels))runner.train_model(train_op,result.\loss,EPOCH_SIZE,\feed_vars=(image_placeholder,\labels_placeholder),\feed_data=pt.train.\feed_numpy(BATCH_SIZE,\train_images,\train_labels),\print_every=100)classification_accuracy = runner.evaluate_model\(accuracy,\TEST_SIZE,\feed_vars=(image_placeholder,\labels_placeholder),\feed_data=pt.train.\feed_numpy(BATCH_SIZE,\test_images,\test_labels))      print("epoch" , epoch + 1)print("accuracy", classification_accuracy )if __name__ == '__main__':make_choice()

運(yùn)行示例,我們必須選擇要訓(xùn)練的模型:

(1) = multylayer model   (2) = LeNet5

通過(guò)選擇multylayer model,我們應(yīng)該具有 95.5% 的準(zhǔn)確率:

Extracting /tmp/data\train-images-idx3-ubyte.gz
Extracting /tmp/data\train-labels-idx1-ubyte.gz
Extracting /tmp/data\t10k-images-idx3-ubyte.gz
Extracting /tmp/data\t10k-labels-idx1-ubyte.gz
epoch 1
accuracy [0.8969]
epoch 2
accuracy [0.914]
epoch 3
accuracy [0.9188]
epoch 4
accuracy [0.9306]
epoch 5
accuracy [0.9353]
epoch 6
accuracy [0.9384]
epoch 7
accuracy [0.9445]
epoch 8
accuracy [0.9472]
epoch 9
accuracy [0.9531]
epoch 10
accuracy [0.9552]

而對(duì)于 Lenet5,我們應(yīng)該具有 98.8% 的準(zhǔn)確率:

Extracting /tmp/data\train-images-idx3-ubyte.gz
Extracting /tmp/data\train-labels-idx1-ubyte.gz
Extracting /tmp/data\t10k-images-idx3-ubyte.gz
Extracting /tmp/data\t10k-labels-idx1-ubyte.gzepoch 1
accuracy [0.9686]
epoch 2
accuracy [0.9755]
epoch 3
accuracy [0.983]
epoch 4
accuracy [0.9841]
epoch 5
accuracy [0.9844]
epoch 6
accuracy [0.9863]
epoch 7
accuracy [0.9862]
epoch 8
accuracy [0.9877]
epoch 9
accuracy [0.9855]
epoch 10
accuracy [0.9886]

Keras

Keras 是一個(gè)用 Python 編寫的開(kāi)源神經(jīng)網(wǎng)絡(luò)庫(kù)。它專注于最小化,模塊化和可擴(kuò)展性,旨在實(shí)現(xiàn) DNN 的快速實(shí)驗(yàn)。

該庫(kù)的主要作者和維護(hù)者是名為 Fran?oisChollet 的 Google 工程師,該庫(kù)是項(xiàng)目 ONEIROS(開(kāi)放式神經(jīng)電子智能機(jī)器人操作系統(tǒng))研究工作的一部分。

Keras 是按照以下設(shè)計(jì)原則開(kāi)發(fā)的:

  • 模塊化:模型被理解為獨(dú)立,完全可配置的模塊序列或圖,可以通過(guò)盡可能少的限制插入到一起。神經(jīng)層,成本函數(shù),優(yōu)化器,初始化方案和激活函數(shù)都是獨(dú)立的模塊,可以組合起來(lái)創(chuàng)建新模型。
  • 極簡(jiǎn)主義:每個(gè)模塊必須簡(jiǎn)短(幾行代碼)并且簡(jiǎn)單。在骯臟的讀取之上,源代碼應(yīng)該是透明的。
  • 可擴(kuò)展性:新模塊易于添加(如新類和函數(shù)),現(xiàn)有模塊提供了基于新模塊的示例。能夠輕松創(chuàng)建新模塊可以實(shí)現(xiàn)完全表現(xiàn)力,使 Keras 適合高級(jí)研究。

Keras 既可以作為 TensorFlow API 在嵌入式版本中使用,也可以作為庫(kù)使用:

  • tf.keras來(lái)自此鏈接

  • Keras v2.1.4(更新和安裝指南請(qǐng)參見(jiàn)此鏈接)

    在以下部分中,我們將了解如何使用第一個(gè)和第二個(gè)實(shí)現(xiàn)。

Keras 編程模型

Keras 的核心數(shù)據(jù)結(jié)構(gòu)是一個(gè)模型,它是一種組織層的方法。有兩種類型的模型:

  • 順序:這只是用于實(shí)現(xiàn)簡(jiǎn)單模型的線性層疊
  • 函數(shù)式 API:用于更復(fù)雜的架構(gòu),例如具有多個(gè)輸出和有向無(wú)環(huán)圖的模型

順序模型

在本節(jié)中,我們將快速通過(guò)向您展示代碼來(lái)解釋順序模型的工作原理。讓我們首先使用 TensorFlow API 導(dǎo)入和構(gòu)建 Keras Sequential模型:

import tensorflow as tf
from tensorflow.python.keras.models import Sequential
model = Sequential()

一旦我們定義了模型,我們就可以添加一個(gè)或多個(gè)層。堆疊操作由add()語(yǔ)句提供:

from keras.layers import Dense, Activation

例如,讓我們添加第一個(gè)完全連接的神經(jīng)網(wǎng)絡(luò)層和激活函數(shù):

model.add(Dense(output_dim=64, input_dim=100))
model.add(Activation("relu"))

然后我們添加第二個(gè)softmax層:

model.add(Dense(output_dim=10))
model.add(Activation("softmax"))

如果模型看起來(lái)很好,我們必須compile()模型,指定損失函數(shù)和要使用的優(yōu)化器:

model.compile(loss='categorical_crossentropy',\optimizer='sgd',\metrics=['accuracy'])

我們現(xiàn)在可以配置我們的優(yōu)化器。 Keras 嘗試使編程變得相當(dāng)簡(jiǎn)單,允許用戶在需要時(shí)完全控制。

編譯完成后,模型必須符合以下數(shù)據(jù):

model.fit(X_train, Y_train, nb_epoch=5, batch_size=32)

或者,我們可以手動(dòng)將批次提供給我們的模型:

model.train_on_batch(X_batch, Y_batch)

一旦訓(xùn)練完成,我們就可以使用我們的模型對(duì)新數(shù)據(jù)進(jìn)行預(yù)測(cè):

classes = model.predict_classes(X_test, batch_size=32)
proba = model.predict_proba(X_test, batch_size=32)

電影評(píng)論的情感分類

在這個(gè)例子中,我們將 Keras 序列模型應(yīng)用于情感分析問(wèn)題。情感分析是破譯書面或口頭文本中包含的觀點(diǎn)的行為。這種技術(shù)的主要目的是識(shí)別詞匯表達(dá)的情感(或極性),這可能具有中性,正面或負(fù)面的含義。我們想要解決的問(wèn)題是 IMDB 電影評(píng)論情感分類問(wèn)題:每個(gè)電影評(píng)論是一個(gè)可變的單詞序列,每個(gè)電影評(píng)論的情感(正面或負(fù)面)必須分類。

問(wèn)題非常復(fù)雜,因?yàn)樾蛄械拈L(zhǎng)度可能不同,并且包含大量的輸入符號(hào)詞匯。該解決方案要求模型學(xué)習(xí)輸入序列中符號(hào)之間的長(zhǎng)期依賴關(guān)系。

IMDB 數(shù)據(jù)集包含 25,000 個(gè)極地電影評(píng)論(好的或壞的)用于訓(xùn)練,并且相同的數(shù)量再次用于測(cè)試。這些數(shù)據(jù)由斯坦福大學(xué)的研究人員收集,并用于 2011 年的一篇論文中,其中五五開(kāi)的數(shù)據(jù)被用于訓(xùn)練和測(cè)試。在本文中,實(shí)現(xiàn)了 88.89% 的準(zhǔn)確率。

一旦我們定義了問(wèn)題,我們就可以開(kāi)發(fā)一個(gè)順序 LSTM 模型來(lái)對(duì)電影評(píng)論的情感進(jìn)行分類。我們可以快速開(kāi)發(fā)用于 IMDB 問(wèn)題的 LSTM 并獲得良好的準(zhǔn)確率。讓我們首先導(dǎo)入此模型所需的類和函數(shù),并將隨機(jī)數(shù)生成器初始化為常量值,以確保我們可以輕松地重現(xiàn)結(jié)果。

在此示例中,我們?cè)?TensorFlow API 中使用嵌入式 Keras:

import numpy
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.datasets import imdb
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import LSTM
from tensorflow.python.keras.layers import Embedding
from tensorflow.python.keras.preprocessing import sequence
numpy.random.seed(7)

我們加載 IMDB 數(shù)據(jù)集。我們將數(shù)據(jù)集限制為前 5,000 個(gè)單詞。我們還將數(shù)據(jù)集分為訓(xùn)練(50%)和測(cè)試(50%)集。

Keras 提供對(duì) IMDB 數(shù)據(jù)集的內(nèi)置訪問(wèn)。imdb.load_data()函數(shù)允許您以準(zhǔn)備好在神經(jīng)網(wǎng)絡(luò)和 DL 模型中使用的格式加載數(shù)據(jù)集。單詞已被整數(shù)替換,這些整數(shù)表示數(shù)據(jù)集中每個(gè)單詞的有序頻率。因此,每個(gè)評(píng)論中的句子包括一系列整數(shù)。

這是代碼:

top_words = 5000
(X_train, y_train), (X_test, y_test) = \imdb.load_data(num_words=top_words)

接下來(lái),我們需要截?cái)嗖⑻畛漭斎胄蛄?#xff0c;以便它們具有相同的建模長(zhǎng)度。該模型將學(xué)習(xí)不攜帶信息的零值,因此序列在內(nèi)容方面的長(zhǎng)度不同,但是向量需要在 Keras 中計(jì)算相同的長(zhǎng)度。每個(gè)評(píng)論中的序列長(zhǎng)度各不相同,因此我們將每個(gè)評(píng)論限制為500單詞,截?cái)嚅L(zhǎng)評(píng)論并用零值填充較短評(píng)論:

讓我們來(lái)看看:

max_review_length = 500
X_train = sequence.pad_sequences\(X_train, maxlen=max_review_length)
X_test = sequence.pad_sequences\(X_test, maxlen=max_review_length)

我們現(xiàn)在可以定義,編譯和調(diào)整我們的 LSTM 模型。

要解決情感分類問(wèn)題,我們將使用單詞嵌入技術(shù)。它包括在連續(xù)向量空間中表示單詞,該向量空間是語(yǔ)義相似的單詞被映射到相鄰點(diǎn)的區(qū)域。單詞嵌入基于分布式假設(shè),該假設(shè)指出出現(xiàn)在給定上下文中的單詞必須具有相同的語(yǔ)義含義。然后將每個(gè)電影評(píng)論映射到真實(shí)的向量域,其中在意義方面的單詞之間的相似性轉(zhuǎn)換為向量空間中的接近度。 Keras 提供了一種通過(guò)使用嵌入層將單詞的正整數(shù)表示轉(zhuǎn)換為單詞嵌入的便捷方式。

在這里,我們定義嵌入向量和模型的長(zhǎng)度:

embedding_vector_length = 32
model = Sequential()

第一層是嵌入層。它使用 32 個(gè)長(zhǎng)度向量來(lái)表示每個(gè)單詞:

model.add(Embedding(top_words, \embedding_vector_length,\input_length=max_review_length))

下一層是具有100存儲(chǔ)單元的 LSTM 層。最后,因?yàn)檫@是一個(gè)分類問(wèn)題,我們使用具有單個(gè)神經(jīng)元的密集輸出層和sigmoid激活函數(shù)來(lái)預(yù)測(cè)問(wèn)題中的類(好的和壞的):

model.add(LSTM(100))
model.add(Dense(1, activation='sigmoid'))

因?yàn)樗嵌诸悊?wèn)題,我們使用binary_crossentropy作為損失函數(shù),而這里使用的優(yōu)化器是adam優(yōu)化算法(我們?cè)谥暗?TensorFlow 實(shí)現(xiàn)中遇到過(guò)它):

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

我們只適合三個(gè)周期,因?yàn)槟P秃芸炀蜁?huì)適應(yīng)。批量大小為 64 的評(píng)論用于分隔權(quán)重更新:

model.fit(X_train, y_train, \validation_data=(X_test, y_test),\num_epochs=3, \batch_size=64)

然后,我們估計(jì)模型在看不見(jiàn)的評(píng)論中的表現(xiàn):

scores = model.evaluate(X_test, y_test, verbose=0)
print("Accuracy: %.2f%%" % (scores[1]*100))

運(yùn)行此示例將生成以下輸出:

Epoch 1/3
16750/16750 [==============================] - 107s - loss: 0.5570 - acc: 0.7149
Epoch 2/3
16750/16750 [==============================] - 107s - loss: 0.3530 - acc: 0.8577
Epoch 3/3
16750/16750 [==============================] - 107s - loss: 0.2559 - acc: 0.9019
Accuracy: 86.79%

您可以看到,這個(gè)簡(jiǎn)單的 LSTM 幾乎沒(méi)有調(diào)整,可以在 IMDB 問(wèn)題上獲得最接近最先進(jìn)的結(jié)果。重要的是,這是一個(gè)模板,您可以使用該模板將 LSTM 網(wǎng)絡(luò)應(yīng)用于您自己的序列分類問(wèn)題。

函數(shù)式 API

為了構(gòu)建復(fù)雜的網(wǎng)絡(luò),我們將在這里描述的函數(shù)式方法非常有用。如第 4 章,卷積神經(jīng)網(wǎng)絡(luò)上的 TensorFlow 所示,最流行的神經(jīng)網(wǎng)絡(luò)(AlexNET,VGG 等)由一個(gè)或多個(gè)重復(fù)多次的神經(jīng)迷你網(wǎng)絡(luò)組成。函數(shù)式 API 包括將神經(jīng)網(wǎng)絡(luò)視為我們可以多次調(diào)用的函數(shù)。這種方法在計(jì)算上是有利的,因?yàn)闉榱藰?gòu)建神經(jīng)網(wǎng)絡(luò),即使是復(fù)雜的神經(jīng)網(wǎng)絡(luò),也只需要幾行代碼。

在以下示例中,我們使用來(lái)自此鏈接的 Keras v2.1.4 。

讓我們看看它是如何工作的。首先,您需要導(dǎo)入Model模塊:

from keras.models import Model

首先要做的是指定模型的輸入。讓我們使用Input()函數(shù)聲明一個(gè)28×28×1的張量:

from keras.layers import Input
digit_input = Input(shape=(28, 28,1))

這是順序模型和函數(shù)式 API 之間的顯著差異之一。因此,使用Conv2DMaxPooling2D API,我們構(gòu)建卷積層:

x = Conv2D(64, (3, 3))(digit_input)
x = Conv2D(64, (3, 3))(x)
x = MaxPooling2D((2, 2))(x)
out = Flatten()(x)

請(qǐng)注意,變量x指定應(yīng)用層的變量。最后,我們通過(guò)指定輸入和輸出來(lái)定義模型:

vision_model = Model(digit_input, out)

當(dāng)然,我們還需要使用fitcompile方法指定損失,優(yōu)化器等,就像我們對(duì)順序模型一樣。

SqueezeNet

在這個(gè)例子中,我們引入了一個(gè)名為 SqueezeNet 的小型 CNN 架構(gòu),它在 ImageNet 上實(shí)現(xiàn)了 AlexNet 級(jí)精度,參數(shù)減少了 50 倍。這個(gè)架構(gòu)的靈感來(lái)自 GoogleNet 的初始模塊,發(fā)表在論文中:SqueezeNet:AlexNet 級(jí)準(zhǔn)確率,參數(shù)減少 50 倍,模型小于 1MB,可從此鏈接下載。

SqueezeNet 背后的想法是減少使用壓縮方案處理的參數(shù)數(shù)量。此策略使用較少的過(guò)濾器減少參數(shù)數(shù)量。這是通過(guò)將擠壓層送入它們所稱的擴(kuò)展層來(lái)完成的。這兩層組成了所謂的 Fire Module,如下圖所示:

SqueezeNet

圖 2:SqueezeNet 消防模塊

fire_module1×1卷積濾波器組成,然后是 ReLU 操作:

x = Convolution2D(squeeze,(1,1),padding='valid', name='fire2/squeeze1x1')(x)
x = Activation('relu', name='fire2/relu_squeeze1x1')(x)

expand部分有兩部分:leftright

left部分使用1×1卷積,稱為擴(kuò)展1×1

left = Conv2D(expand, (1, 1), padding='valid', name=s_id + exp1x1)(x)
left = Activation('relu', name=s_id + relu + exp1x1)(left)

right部分使用3×3卷積,稱為expand3x3。這兩個(gè)部分后面都是 ReLU 層:

right = Conv2D(expand, (3, 3), padding='same', name=s_id + exp3x3)(x)
right = Activation('relu', name=s_id + relu + exp3x3)(right)

消防模塊的最終輸出是左右連接:

x = concatenate([left, right], axis=channel_axis, name=s_id + 'concat')

然后,重復(fù)使用fire_module構(gòu)建完整的網(wǎng)絡(luò),如下所示:

x = Convolution2D(64,(3,3),strides=(2,2), padding='valid',\ name='conv1')(img_input)
x = Activation('relu', name='relu_conv1')(x)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool1')(x)x = fire_module(x, fire_id=2, squeeze=16, expand=64)
x = fire_module(x, fire_id=3, squeeze=16, expand=64)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool3')(x)x = fire_module(x, fire_id=4, squeeze=32, expand=128)
x = fire_module(x, fire_id=5, squeeze=32, expand=128)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool5')(x)x = fire_module(x, fire_id=6, squeeze=48, expand=192)
x = fire_module(x, fire_id=7, squeeze=48, expand=192)
x = fire_module(x, fire_id=8, squeeze=64, expand=256)
x = fire_module(x, fire_id=9, squeeze=64, expand=256)
x = Dropout(0.5, name='drop9')(x)x = Convolution2D(classes, (1, 1), padding='valid', name='conv10')(x)
x = Activation('relu', name='relu_conv10')(x)
x = GlobalAveragePooling2D()(x)
x = Activation('softmax', name='loss')(x)
model = Model(inputs, x, name='squeezenet')

下圖顯示了 SqueezeNet 架構(gòu):

SqueezeNet

圖 3:SqueezeNet 架構(gòu)

您可以從下面的鏈接執(zhí)行 Keras 的 SqueezeNet(在squeezenet.py文件):

然后我們?cè)谝韵?code>squeeze_test.jpg(227×227)圖像上測(cè)試模型:

SqueezeNet

圖 4:SqueezeNet 測(cè)試圖像

我們只需使用以下幾行代碼即可:

import os
import numpy as np
import squeezenet as sq
from keras.applications.imagenet_utils import preprocess_input
from keras.applications.imagenet_utils import preprocess_input, decode_predictions
from keras.preprocessing import imagemodel = sq.SqueezeNet()
img = image.load_img('squeeze_test.jpg', target_size=(227, 227))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)preds = model.predict(x)
print('Predicted:', decode_predictions(preds))

如您所見(jiàn),結(jié)果非常有趣:

Predicted: [[('n02504013', 'Indian_elephant', 0.64139527), ('n02504458', 'African_elephant', 0.22846894), ('n01871265', 'tusker', 0.12922771), ('n02397096', 'warthog', 0.00037213496), ('n02408429', 'water_buffalo', 0.00032306617)]]

總結(jié)

在本章中,我們研究了一些基于 TensorFlow 的 DL 研究和開(kāi)發(fā)庫(kù)。我們引入了tf.estimator,它是 DL/ML 的簡(jiǎn)化接口,現(xiàn)在是 TensorFlow 和高級(jí) ML API 的一部分,可以輕松訓(xùn)練,配置和評(píng)估各種 ML 模型。我們使用估計(jì)器函數(shù)為鳶尾花數(shù)據(jù)集實(shí)現(xiàn)分類器。

我們還看了一下 TFLearn 庫(kù),它包含了很多 TensorFlow API。在這個(gè)例子中,我們使用 TFLearn 來(lái)估計(jì)泰坦尼克號(hào)上乘客的生存機(jī)會(huì)。為了解決這個(gè)問(wèn)題,我們構(gòu)建了一個(gè) DNN 分類器。

然后,我們介紹了 PrettyTensor,它允許 TensorFlow 操作被包裝以鏈接任意數(shù)量的層。我們以 LeNet 的風(fēng)格實(shí)現(xiàn)了卷積模型,以快速解決手寫分類模型。

然后我們快速瀏覽了 Keras,它設(shè)計(jì)用于極簡(jiǎn)主義和模塊化,允許用戶快速定義 DL 模型。使用 Keras,我們學(xué)習(xí)了如何為 IMDB 電影評(píng)論情感分類問(wèn)題開(kāi)發(fā)一個(gè)簡(jiǎn)單的單層 LSTM 模型。在最后一個(gè)例子中,我們使用 Keras 的函數(shù)從預(yù)訓(xùn)練的初始模型開(kāi)始構(gòu)建 SqueezeNet 神經(jīng)網(wǎng)絡(luò)。

下一章將介紹強(qiáng)化學(xué)習(xí)。我們將探討強(qiáng)化學(xué)習(xí)的基本原理和算法。我們還將看到一些使用 TensorFlow 和 OpenAI Gym 框架的示例,這是一個(gè)用于開(kāi)發(fā)和比較強(qiáng)化學(xué)習(xí)算法的強(qiáng)大工具包。

九、協(xié)同過(guò)濾和電影推薦

在這一部分,我們將看到如何利用協(xié)同過(guò)濾來(lái)開(kāi)發(fā)推薦引擎。然而,在此之前讓我們討論偏好的效用矩陣。

效用矩陣

在基于協(xié)同過(guò)濾的推薦系統(tǒng)中,存在實(shí)體的維度:用戶和項(xiàng)目(項(xiàng)目指的是諸如電影,游戲和歌曲的產(chǎn)品)。作為用戶,您可能對(duì)某些項(xiàng)目有首選項(xiàng)。因此,必須從有關(guān)項(xiàng)目,用戶或評(píng)級(jí)的數(shù)據(jù)中提取這些首選項(xiàng)。該數(shù)據(jù)通常表示為效用矩陣,例如用戶 - 項(xiàng)目對(duì)。這種類型的值可以表示關(guān)于用戶對(duì)特定項(xiàng)目的偏好程度的已知信息。

矩陣中的條目可以來(lái)自有序集。例如,整數(shù) 1-5 可用于表示用戶在評(píng)價(jià)項(xiàng)目時(shí)給出的星數(shù)。我們已經(jīng)提到用戶可能不會(huì)經(jīng)常評(píng)價(jià)項(xiàng)目,因此大多數(shù)條目都是未知的。因此,將 0 分配給未知項(xiàng)將失敗,這也意味著矩陣可能是稀疏的。未知評(píng)級(jí)意味著我們沒(méi)有關(guān)于用戶對(duì)項(xiàng)目的偏好的明確信息。

表 1 顯示示例效用矩陣。矩陣表示用戶以 1-5 分為單位給予電影的評(píng)級(jí),其中 5 為最高評(píng)級(jí)。空白條目表示特定用戶未為該特定電影提供任何評(píng)級(jí)的事實(shí)。 HP1,HP2 和 HP3 是 Harry Potter I,II 和 III 的首字母縮略詞,TW 代表 Twilight,SW1,SW2 和 SW3 代表星球大戰(zhàn)的第 1,2 和 3 集。字母 A,B,C 和 D 代表用戶:

The utility matrix

表 1:效用矩陣(用戶與電影矩陣)

用戶電影對(duì)有許多空白條目。這意味著用戶尚未對(duì)這些電影進(jìn)行評(píng)級(jí)。在現(xiàn)實(shí)生活中,矩陣可能更稀疏,典型的用戶評(píng)級(jí)只是所有可用電影的一小部分。使用此矩陣,目標(biāo)是預(yù)測(cè)效用矩陣中的空白?,F(xiàn)在,讓我們看一個(gè)例子。假設(shè)我們很想知道用戶 A 是否想要 SW2。很難解決這個(gè)問(wèn)題,因?yàn)楸?1 中的矩陣內(nèi)沒(méi)有太多數(shù)據(jù)可供使用。

因此,在實(shí)踐中,我們可能會(huì)開(kāi)發(fā)一個(gè)電影推薦引擎來(lái)考慮電影的其他屬性,例如制片人,導(dǎo)演,主要演員,甚至是他們名字的相似性。這樣,我們可以計(jì)算電影 SW1 和 SW2 的相似度。這種相似性會(huì)讓我們得出結(jié)論,因?yàn)?A 不喜歡 SW1,所以他們也不太可能喜歡 SW2。

但是,這可能不適用于更大的數(shù)據(jù)集。因此,有了更多的數(shù)據(jù),我們可能會(huì)發(fā)現(xiàn),對(duì) SW1 和 SW2 進(jìn)行評(píng)級(jí)的人都傾向于給予他們相似的評(píng)級(jí)。最后,我們可以得出結(jié)論,A 也會(huì)給 SW2 一個(gè)低評(píng)級(jí),類似于 A 的 SW1 評(píng)級(jí)。

在下一節(jié)中,我們將看到如何使用協(xié)同過(guò)濾方法開(kāi)發(fā)電影推薦引擎。我們將看到如何利用這種類型的矩陣。

注意

如何使用代碼庫(kù):此代碼庫(kù)中有八個(gè) Python 腳本(即使用TensorFlow_09_Codes/CollaborativeFiltering進(jìn)行深度學(xué)習(xí))。首先,執(zhí)行執(zhí)行數(shù)據(jù)集探索性分析的eda.py。然后,調(diào)用train.py腳本執(zhí)行訓(xùn)練。最后,Test.py可用于模型推理和評(píng)估。

以下是每個(gè)腳本的簡(jiǎn)要功能:

  • eda.py:這用于 MovieLens1M 數(shù)據(jù)集的探索性分析。
  • train.py:它執(zhí)行訓(xùn)練和驗(yàn)證。然后它打印驗(yàn)證誤差。最后,它創(chuàng)建了用戶項(xiàng)密集表。
  • Test.py:恢復(fù)訓(xùn)練中生成的用戶與項(xiàng)目表。然后評(píng)估所有模型。
  • run.py:用于模型推理并進(jìn)行預(yù)測(cè)。
  • kmean.py:它集中了類似的電影。
  • main.py:它計(jì)算前 K 個(gè)電影,創(chuàng)建用戶評(píng)級(jí),找到前 K 個(gè)相似項(xiàng)目,計(jì)算用戶相似度,計(jì)算項(xiàng)目相關(guān)性并計(jì)算用戶 Pearson 相關(guān)性。
  • readers.py:它讀取評(píng)級(jí)和電影數(shù)據(jù)并執(zhí)行一些預(yù)處理。最后,它為批量訓(xùn)練準(zhǔn)備數(shù)據(jù)集。
  • model.py:它創(chuàng)建模型并計(jì)算訓(xùn)練/驗(yàn)證損失。

工作流程可以描述如下:

  1. 首先,我們將使用可用的評(píng)級(jí)來(lái)訓(xùn)練模型。
  2. 然后我們使用訓(xùn)練的模型來(lái)預(yù)測(cè)用戶與電影矩陣中的缺失評(píng)級(jí)。
  3. 然后,利用所有預(yù)測(cè)的評(píng)級(jí),將構(gòu)建新用戶與電影矩陣并以.pkl文件的形式保存。
  4. 然后,我們使用此矩陣來(lái)預(yù)測(cè)特定用戶的評(píng)級(jí)。
  5. 最后,我們將訓(xùn)練 K 均值模型來(lái)聚類相關(guān)電影。

數(shù)據(jù)集的描述

在我們開(kāi)始實(shí)現(xiàn)電影 RE 之前,讓我們看一下將要使用的數(shù)據(jù)集。 MovieLens1M 數(shù)據(jù)集從 MovieLens 網(wǎng)站下載。

我真誠(chéng)地感謝 F. Maxwell Harper 和 Joseph A. Konstan 使數(shù)據(jù)集可供使用。數(shù)據(jù)集發(fā)布在 MovieLens 數(shù)據(jù)集:歷史和上下文中。 ACM 交互式智能系統(tǒng)交易(TiiS)5,4,第 19 條(2015 年 12 月),共 19 頁(yè)。

數(shù)據(jù)集中有三個(gè)文件,它們與電影,評(píng)級(jí)和用戶有關(guān)。這些文件包含 1,000,209 個(gè)匿名評(píng)級(jí),約有 3,900 部電影,由 2000 年加入 MovieLens 的 6,040 名 MovieLens 用戶制作。

評(píng)級(jí)數(shù)據(jù)

所有評(píng)級(jí)都包含在ratings.dat文件中,格式如下 - UserID :: MovieID :: Rating :: Timestamp

  • UserID的范圍在 1 到 6,040 之間
  • MovieID的范圍在 1 到 3,952 之間
  • Rating為五星級(jí)
  • Timestamp以秒表示

請(qǐng)注意,每位用戶至少評(píng)了 20 部電影。

電影數(shù)據(jù)

電影信息是movies.dat文件中的 ,格式如下 - MovieID :: Title :: Genres

  • 標(biāo)題與 IMDb 提供的標(biāo)題相同(發(fā)布年份)
  • 流派是分開(kāi)的,每部電影分為動(dòng)作,冒險(xiǎn),動(dòng)畫,兒童,喜劇,犯罪,戲劇,戰(zhàn)爭(zhēng),紀(jì)錄片,幻想,電影黑色,恐怖,音樂(lè),神秘,浪漫,科幻菲律賓,驚悚片和西部片

用戶數(shù)據(jù)

用戶信息位于users.dat文件中,格式如下:UserID :: Gender :: Age :: Occupation :: Zip-code

所有人口統(tǒng)計(jì)信息均由用戶自愿提供,不會(huì)檢查其準(zhǔn)確率。此數(shù)據(jù)集中僅包含已提供某些人口統(tǒng)計(jì)信息的用戶。男性 M 和女性 F 表示性別。

年齡選自以下范圍:

  • 1:18 歲以下
  • 18:18-24
  • 25:25-34
  • 35:35-44
  • 45:45-49
  • 50:50-55
  • 56:56+

職業(yè)選自以下選項(xiàng):

0:其他,或未指定

1:學(xué)術(shù)/教育者

2:藝術(shù)家

3:文員/管理員

4:大學(xué)/研究生

5:客戶服務(wù)

6:醫(yī)生/保健

7:執(zhí)行/管理

8:農(nóng)民

9:主婦

10:K-12 學(xué)生

11:律師

12:程序員

13:退休了

14:銷售/營(yíng)銷

15:科學(xué)家

16:自雇人士

17:技師/工程師

18:匠人/工匠

19:失業(yè)

20:作家

對(duì) MovieLens 數(shù)據(jù)集的探索性分析

在這里,在我們開(kāi)始開(kāi)發(fā) RE 之前,我們將看到數(shù)據(jù)集的探索性描述。我假設(shè)讀者已經(jīng)從此鏈接下載了 MovieLens1m 數(shù)據(jù)集并將其解壓縮到此代碼庫(kù)中的輸入目錄?,F(xiàn)在,為此,在終端上執(zhí)行$ python3 eda.py命令:

  1. 首先,讓我們導(dǎo)入所需的庫(kù)和包:

    import matplotlib.pyplot as plt
    import seaborn as sns
    import pandas as pd
    import numpy as np
    
  2. 現(xiàn)在讓我們加載用戶,評(píng)級(jí)和電影數(shù)據(jù)集并創(chuàng)建一個(gè) pandas DataFrame

    ratings_list = [i.strip().split("::") for i in open('Input/ratings.dat', 'r').readlines()]
    users_list = [i.strip().split("::") for i in open('Input/users.dat', 'r').readlines()]
    movies_list = [i.strip().split("::") for i in open('Input/movies.dat', 'r',encoding='latin-1').readlines()]
    ratings_df = pd.DataFrame(ratings_list, columns = ['UserID', 'MovieID', 'Rating', 'Timestamp'], dtype = int)
    movies_df = pd.DataFrame(movies_list, columns = ['MovieID', 'Title', 'Genres'])
    user_df=pd.DataFrame(users_list, columns=['UserID','Gender','Age','Occupation','ZipCode'])
    
  3. 接下來(lái)的任務(wù)是使用內(nèi)置的to_numeric() pandas 函數(shù)將分類列(如MovieIDUserIDAge)轉(zhuǎn)換為數(shù)值:

    movies_df['MovieID'] = movies_df['MovieID'].apply(pd.to_numeric)
    user_df['UserID'] = user_df['UserID'].apply(pd.to_numeric)
    user_df['Age'] = user_df['Age'].apply(pd.to_numeric)
    
  4. 讓我們看一下用戶表中的一些例子:

    print("User table description:")
    print(user_df.head())
    print(user_df.describe())
    >>>
    User table description:
    UserID Gender  Age      Occupation ZipCode1            F         1         10                480672            M        56       16                700723           M         25       15                 551174           M         45       7                  024605           M        25       20                 55455UserID          Age
    count  6040.000000  6040.000000
    mean   3020.500000    30.639238
    std    1743.742145    12.895962
    min       1.000000     1.000000
    25%    1510.750000    25.000000
    50%    3020.500000    25.000000
    75%    4530.250000    35.000000
    max    6040.000000    56.000000
    
  5. 讓我們看一下來(lái)自評(píng)級(jí)數(shù)據(jù)集的一些信息:

    print("Rating table description:")
    print(ratings_df.head())
    print(ratings_df.describe())
    >>>
    Rating table description:
    UserID  MovieID  Rating  Timestamp1     1193              5           9783007601      661               3           9783021091      914               3           9783019681     3408              4           9783002751     2355              5           978824291UserID       MovieID        Rating     Timestamp
    count  1.000209e+06  1.000209e+06  1.000209e+06  1.000209e+06
    mean   3.024512e+03  1.865540e+03  3.581564e+00  9.722437e+08
    std    1.728413e+03  1.096041e+03  1.117102e+00  1.215256e+07
    min    1.000000e+00  1.000000e+00  1.000000e+00  9.567039e+08
    25%    1.506000e+03  1.030000e+03  3.000000e+00  9.653026e+08
    50%    3.070000e+03  1.835000e+03  4.000000e+00  9.730180e+08
    75%    4.476000e+03  2.770000e+03  4.000000e+00  9.752209e+08
    max    6.040000e+03  3.952000e+03  5.000000e+00  1.046455e+09
    
  6. 讓我們看看電影數(shù)據(jù)集中的一些信息:

    >>>
    print("Movies table description:")
    print(movies_df.head())
    print(movies_df.describe())
    >>>
    Movies table description:MovieID                Title                                               Genres
    0        1             Toy Story (1995)                   Animation|Children's|Comedy
    1        2             Jumanji (1995)                      Adventure|Children's|Fantasy
    2        3             Grumpier Old Men (1995)                Comedy|Romance
    3        4            Waiting to Exhale (1995)                  Comedy|Drama
    4        5            Father of the Bride Part II (1995)            ComedyMovieID
    count  3883.000000
    mean   1986.049446
    std    1146.778349
    min       1.000000
    25%     982.500000
    50%    2010.000000
    75%    2980.500000
    max    3952.000000
    
  7. 現(xiàn)在讓我們看看評(píng)級(jí)最高的五部電影:

    print("Top ten most rated movies:")
    print(ratings_df['MovieID'].value_counts().head())
    >>>
    Top 10 most rated movies with title and rating count:American Beauty (1999)                                                      3428
    Star Wars: Episode IV - A New Hope (1977)                      2991
    Star Wars: Episode V - The Empire Strikes Back (1980)    2990
    Star Wars: Episode VI - Return of the Jedi (1983)              2883
    Jurassic Park (1993)                                                             2672
    Saving Private Ryan (1998)                                                 2653
    Terminator 2: Judgment Day (1991)                                    2649
    Matrix, The (1999)                                                               2590
    Back to the Future (1985)                                                    2583
    Silence of the Lambs, The (1991)                                        2578
    
  8. 現(xiàn)在,讓我們看一下電影評(píng)級(jí)分布。 為此,讓我們使用直方圖,該圖演示了一個(gè)重要的模式,票數(shù)呈正態(tài)分布:

    plt.hist(ratings_df.groupby(['MovieID'])['Rating'].mean().sort_values(axis=0, ascending=False))
    plt.title("Movie rating Distribution")
    plt.ylabel('Count of movies')
    plt.xlabel('Rating');
    plt.show()
    >>>
    

    Exploratory analysis of the MovieLens dataset

    圖 3:電影評(píng)級(jí)分布

  9. 讓我們看看評(píng)級(jí)如何分布在不同年齡段:

    user_df.Age.plot.hist()
    plt.title("Distribution of users (by ages)")
    plt.ylabel('Count of users')
    plt.xlabel('Age');
    plt.show()
    >>>
    

    Exploratory analysis of the MovieLens dataset

    圖 4:按年齡分布的用戶

  10. 現(xiàn)在讓我們看看收視率最高的電影,評(píng)級(jí)至少為 150:

```py
movie_stats = df.groupby('Title').agg({'Rating': [np.size, np.mean]})
print("Highest rated movie with minimum 150 ratings")
print(movie_stats.Rating[movie_stats.Rating['size'] > 150].sort_values(['mean'],ascending=[0]).head())
>>>
Top 5 and a highest rated movie with a minimum of 150 ratings-----------------------------------------------------------
Title                                                               size 	  mean                                      
Seven Samurai (The Magnificent Seven)    628   4.560510
Shawshank Redemption, The (1994)           2227  4.554558
Godfather, The (1972)                                 2223  4.524966
Close Shave, A (1995)                                 657    4.520548
Usual Suspects, The (1995)                         1783   4.517106
```
  1. 讓我們看看在電影評(píng)級(jí)中的性別偏置,即電影評(píng)級(jí)如何按評(píng)論者的性別進(jìn)行比較:
```py
>>>
pivoted = df.pivot_table(index=['MovieID', 'Title'], columns=['Gender'], values='Rating', fill_value=0)
print("Gender biasing towards movie rating")
print(pivoted.head())
```
  1. 我們現(xiàn)在可以看看電影收視率的性別偏置及它們之間的差異,即男性和女性對(duì)電影的評(píng)價(jià)方式不同:
```py
pivoted['diff'] = pivoted.M - pivoted.F
print(pivoted.head())
>>>
Gender                                                   F                       M                      diff
MovieID Title                                                           
1  Toy Story (1995)                            4.87817        4.130552                -0.057265
2  Jumanji (1995)                               3.278409      3.175238                -0.103171
3  Grumpier Old Men (1995)             3.073529      2.994152               -0.079377
4  Waiting to Exhale (1995)              2.976471        2.482353              -0.494118
5  Father of the Bride Part II (1995)  3.212963       2.888298               -0.324665
```
  1. 從前面的輸出中可以清楚地看出,在大多數(shù)情況下,男性的收視率高于女性?,F(xiàn)在我們已經(jīng)看到了有關(guān)數(shù)據(jù)集的一些信息和統(tǒng)計(jì)數(shù)據(jù),現(xiàn)在是構(gòu)建我們的 TensorFlow 推薦模型的時(shí)候了。

實(shí)現(xiàn)電影 RE

在這個(gè)例子中,我們將看到如何推薦前 K 部電影(其中 K 是電影數(shù)量),預(yù)測(cè)用戶評(píng)級(jí)并推薦前 K 個(gè)類似項(xiàng)目(其中 K 是項(xiàng)目數(shù))。然后我們將看到如何計(jì)算用戶相似度。

然后我們將使用 Pearson 的相關(guān)算法看到項(xiàng)目項(xiàng)相關(guān)性和用戶 - 用戶相關(guān)性。最后,我們將看到如何使用 K 均值算法對(duì)類似的電影進(jìn)行聚類。

換句話說(shuō),我們將使用協(xié)同過(guò)濾方法制作電影推薦引擎,并使用 K 均值來(lái)聚類類似的電影。

距離計(jì)算:還有其他計(jì)算距離的方法。例如:

  1. 通過(guò)僅考慮最顯著的尺寸,可以使用切比雪夫距離來(lái)測(cè)量距離。
  2. 漢明距離算法可以識(shí)別兩個(gè)字符串之間的差異。
  3. 馬哈拉諾比斯距離可用于歸一化協(xié)方差矩陣。
  4. 曼哈頓距離用于通過(guò)僅考慮軸對(duì)齊方向來(lái)測(cè)量距離。
  5. Haversine 距離用于測(cè)量球體上兩個(gè)點(diǎn)之間的大圓距離。

考慮到這些距離測(cè)量算法,很明顯歐幾里德距離算法最適合解決 K 均值算法中距離計(jì)算的目的。

總之,以下是用于開(kāi)發(fā)此模型的工作流程:

  1. 首先,使用可用的評(píng)級(jí)來(lái)訓(xùn)練模型。
  2. 使用該訓(xùn)練模型來(lái)預(yù)測(cè)用戶與電影矩陣中的缺失評(píng)級(jí)。
  3. 利用所有預(yù)測(cè)的評(píng)級(jí),用戶與電影矩陣成為受訓(xùn)用戶與電影矩陣,我們以.pkl文件的形式保存。
  4. 然后,我們使用用戶與電影矩陣,或訓(xùn)練用戶與電影矩陣的訓(xùn)練參數(shù),進(jìn)行進(jìn)一步處理。

在訓(xùn)練模型之前,第一項(xiàng)工作是利用所有可用的數(shù)據(jù)集來(lái)準(zhǔn)備訓(xùn)練集。

使用可用的評(píng)級(jí)訓(xùn)練模型

對(duì)于這一部分,請(qǐng)使用train.py腳本,該腳本依賴于其他腳本。我們將看到依賴項(xiàng):

  1. 首先,讓我們導(dǎo)入必要的包和模塊:

    from collections import deque
    from six import next
    import readers
    import os
    import tensorflow as tf
    import numpy as np
    import model as md
    import pandas as pd
    import time
    import matplotlib.pyplot as plt
    
  2. 然后我們?cè)O(shè)置隨機(jī)種子的復(fù)現(xiàn)性:

    np.random.seed(12345)
    
  3. 下一個(gè)任務(wù)是定義訓(xùn)練參數(shù)。讓我們定義所需的數(shù)據(jù)參數(shù),例如評(píng)級(jí)數(shù)據(jù)集的位置,批量大小,SVD 的維度,最大周期和檢查點(diǎn)目錄:

    data_file ="Input/ratings.dat"# Input user-movie-rating information file
    batch_size = 100 #Batch Size (default: 100)
    dims =15 #Dimensions of SVD (default: 15)
    max_epochs = 50 # Maximum epoch (default: 25)
    checkpoint_dir ="save/" #Checkpoint directory from training runval = True #True if Folders with files and False if single file
    is_gpu = True # Want to train model with GPU
    
  4. 我們還需要一些其他參數(shù),例如允許軟放置和日志設(shè)備放置:

    allow_soft_placement = True #Allow device soft device placement
    log_device_placement=False #Log placement of ops on devices
    
  5. 我們不想用舊的元數(shù)據(jù)或檢查點(diǎn)和模型文件污染我們的新訓(xùn)練 ,所以如果有的話,讓我們刪除它們:

    print("Start removing previous Files ...")
    if os.path.isfile("model/user_item_table.pkl"):os.remove("model/user_item_table.pkl")
    if os.path.isfile("model/user_item_table_train.pkl"):os.remove("model/user_item_table_train.pkl")
    if os.path.isfile("model/item_item_corr.pkl"):os.remove("model/item_item_corr.pkl")
    if os.path.isfile("model/item_item_corr_train.pkl"):os.remove("model/item_item_corr_train.pkl")
    if os.path.isfile("model/user_user_corr.pkl"):os.remove("model/user_user_corr.pkl")
    if os.path.isfile("model/user_user_corr_train.pkl"):os.remove("model/user_user_corr_train.pkl")
    if os.path.isfile("model/clusters.csv"):os.remove("model/clusters.csv")
    if os.path.isfile("model/val_error.pkl"):os.remove("model/val_error.pkl")
    print("Done ...")
    >>>
    Start removing previous Files...
    Done...
    
  6. 然后讓我們定義檢查點(diǎn)目錄。 TensorFlow 假設(shè)此目錄已存在,因此我們需要?jiǎng)?chuàng)建它:

    checkpoint_prefix = os.path.join(checkpoint_dir, "model")
    if not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir)
    
  7. 在進(jìn)入數(shù)據(jù)之前,讓我們?cè)O(shè)置每批的樣本數(shù)量,數(shù)據(jù)的維度以及網(wǎng)絡(luò)看到所有訓(xùn)練數(shù)據(jù)的次數(shù) :

    batch_size =batch_size
    dims =dims
    max_epochs =max_epochs
    
  8. 現(xiàn)在讓我們指定用于所有 TensorFlow 計(jì)算,CPU 或 GPU 的設(shè)備:

    if is_gpu:place_device = "/gpu:0"
    else:place_device="/cpu:0"
    
  9. 現(xiàn)在我們通過(guò)get_data()函數(shù)讀取帶有分隔符::的評(píng)級(jí)文件。示例列包含用戶 ID,項(xiàng) ID,評(píng)級(jí)和時(shí)間戳,例如3 :: 1196 :: 4 :: 978297539。然后,上面的代碼執(zhí)行純粹基于整數(shù)位置的索引,以便按位置進(jìn)行選擇。之后,它將數(shù)據(jù)分為訓(xùn)練和測(cè)試,75% 用于訓(xùn)練,25% 用于測(cè)試。最后,它使用索引來(lái)分離數(shù)據(jù)并返回用于訓(xùn)練的數(shù)據(jù)幀:

    def get_data():print("Inside get data ...")df = readers.read_file(data_file, sep="::")rows = len(df)df = df.iloc[np.random.permutation(rows)].reset_index(drop=True)split_index = int(rows * 0.75)df_train = df[0:split_index]df_test = df[split_index:].reset_index(drop=True)print("Done !!!")print(df.shape)return df_train, df_test,df['user'].max(),df['item'].max()
    
  10. 然后,我們?cè)跀?shù)組中剪切值的限制:給定一個(gè)間隔,將間隔外的值剪切到間隔的邊緣。例如,如果指定[0,1]間隔,則小于 0 的值變?yōu)?0,大于 1 的值變?yōu)?1:

```py
def clip(x):return np.clip(x, 1.0, 5.0)
```

然后,我們調(diào)用read_data()方法從評(píng)級(jí)文件中讀取數(shù)據(jù)以構(gòu)建 TensorFlow 模型:

df_train, df_test,u_num,i_num = get_data()
>>>
Inside get data...
Done!!!
  1. 然后,我們定義數(shù)據(jù)集中評(píng)級(jí)電影的用戶數(shù)量,以及數(shù)據(jù)集中的電影數(shù)量:

    u_num = 6040 # Number of users in the dataset
    i_num = 3952 # Number of movies in the dataset
    
  2. 現(xiàn)在讓我們生成每批樣本數(shù)量:

    samples_per_batch = len(df_train) // batch_size
    print("Number of train samples %d, test samples %d, samples per batch %d" % (len(df_train), len(df_test), samples_per_batch))
    >>>
    Number of train samples 750156, test samples 250053, samples per batch 7501
    
  3. 現(xiàn)在,使用ShuffleIterator,我們生成隨機(jī)批次。在訓(xùn)練中,這有助于防止偏差結(jié)果以及過(guò)擬合:

    iter_train = readers.ShuffleIterator([df_train["user"], df_train["item"],df_train["rate"]], batch_size=batch_size)
    
  4. 有關(guān)此類的更多信息,請(qǐng)參閱readers.py腳本。為方便起見(jiàn),以下是此類的來(lái)源:

    class ShuffleIterator(object):def __init__(self, inputs, batch_size=10):self.inputs = inputsself.batch_size = batch_sizeself.num_cols = len(self.inputs)self.len = len(self.inputs[0])self.inputs = np.transpose(np.vstack([np.array(self.inputs[i]) for i in range(self.num_cols)]))def __len__(self):return self.lendef __iter__(self):return selfdef __next__(self):return self.next()def next(self):ids = np.random.randint(0, self.len, (self.batch_size,))out = self.inputs[ids, :]return [out[:, i] for i in range(self.num_cols)]
    
  5. 然后我們依次生成一個(gè)周期的批次進(jìn)行測(cè)試(參見(jiàn)train.py):

    iter_test = readers.OneEpochIterator([df_test["user"], df_test["item"], df_test["rate"]], batch_size=-1)
    
  6. 有關(guān)此類的更多信息,請(qǐng)參閱readers.py腳本。為了方便,這里是這個(gè)類的源碼:

    class OneEpochIterator(ShuffleIterator):def __init__(self, inputs, batch_size=10):super(OneEpochIterator, self).__init__(inputs, batch_size=batch_size)if batch_size > 0:self.idx_group = np.array_split(np.arange(self.len), np.ceil(self.len / batch_size))else:self.idx_group = [np.arange(self.len)]self.group_id = 0def next(self):if self.group_id >= len(self.idx_group):self.group_id = 0raise StopIterationout = self.inputs[self.idx_group[self.group_id], :]self.group_id += 1return [out[:, i] for i in range(self.num_cols)]
    
  7. 現(xiàn)在是創(chuàng)建 TensorFlow 占位符的時(shí)間:

    user_batch = tf.placeholder(tf.int32, shape=[None], name="id_user")
    item_batch = tf.placeholder(tf.int32, shape=[None], name="id_item")
    rate_batch = tf.placeholder(tf.float32, shape=[None])
    
  8. 現(xiàn)在我們的訓(xùn)練集和占位符已準(zhǔn)備好容納訓(xùn)練值的批次,現(xiàn)在該實(shí)例化模型了。 為此,我們使用model()方法并使用 l2 正則化來(lái)避免過(guò)擬合(請(qǐng)參見(jiàn)model.py腳本):

    infer, regularizer = md.model(user_batch, item_batch, user_num=u_num, item_num=i_num, dim=dims, device=place_device)
    

    model()方法如下:

    def model(user_batch, item_batch, user_num, item_num, dim=5, device="/cpu:0"):with tf.device("/cpu:0"):# Using a global bias termbias_global = tf.get_variable("bias_global", shape=[])# User and item bias variables: get_variable: Prefixes the name with the current variable # scope and performs reuse checks.w_bias_user = tf.get_variable("embd_bias_user", shape=[user_num])w_bias_item = tf.get_variable("embd_bias_item", shape=[item_num])# embedding_lookup: Looks up 'ids' in a list of embedding tensors# Bias embeddings for user and items, given a batchbias_user = tf.nn.embedding_lookup(w_bias_user, user_batch, name="bias_user")bias_item = tf.nn.embedding_lookup(w_bias_item, item_batch, name="bias_item")# User and item weight variablesw_user = tf.get_variable("embd_user", shape=[user_num, dim],initializer=tf.truncated_normal_initializer(stddev=0.02))w_item = tf.get_variable("embd_item", shape=[item_num, dim],initializer=tf.truncated_normal_initializer(stddev=0.02))# Weight embeddings for user and items, given a batchembd_user = tf.nn.embedding_lookup(w_user, user_batch, name="embedding_user")embd_item = tf.nn.embedding_lookup(w_item, item_batch, name="embedding_item")# reduce_sum: Computes the sum of elements across dimensions of a tensorinfer = tf.reduce_sum(tf.multiply(embd_user, embd_item), 1)infer = tf.add(infer, bias_global)infer = tf.add(infer, bias_user)infer = tf.add(infer, bias_item, name="svd_inference")# l2_loss: Computes half the L2 norm of a tensor without the sqrtregularizer = tf.add(tf.nn.l2_loss(embd_user), tf.nn.l2_loss(embd_item), name="svd_regularizer")return infer, regularizer
    
  9. 現(xiàn)在讓我們定義訓(xùn)練操作(參見(jiàn)models.py腳本中的更多內(nèi)容):

    _, train_op = md.loss(infer, regularizer, rate_batch, learning_rate=0.001, reg=0.05, device=place_device)
    

loss()方法如下:

def loss(infer, regularizer, rate_batch, learning_rate=0.1, reg=0.1, device="/cpu:0"):with tf.device(device):cost_l2 = tf.nn.l2_loss(tf.subtract(infer, rate_batch))penalty = tf.constant(reg, dtype=tf.float32, shape=[], name="l2")cost = tf.add(cost_l2, tf.multiply(regularizer, penalty))train_op = tf.train.FtrlOptimizer(learning_rate).minimize(cost)return cost, train_op
  1. 一旦我們實(shí)例化了模型和訓(xùn)練操作,我們就可以保存模型以備將來(lái)使用:

    saver = tf.train.Saver()
    init_op = tf.global_variables_initializer()
    session_conf = tf.ConfigProto(allow_soft_placement=allow_soft_placement, log_device_placement=log_device_placement)
    
  2. 現(xiàn)在我們開(kāi)始訓(xùn)練模型:

    with tf.Session(config = session_conf) as sess:sess.run(init_op)print("%s\t%s\t%s\t%s" % ("Epoch", "Train err", "Validation err", "Elapsed Time"))errors = deque(maxlen=samples_per_batch)train_error=[]val_error=[]start = time.time()for i in range(max_epochs * samples_per_batch):users, items, rates = next(iter_train)_, pred_batch = sess.run([train_op, infer], feed_dict={user_batch: users, item_batch: items, rate_batch: rates})pred_batch = clip(pred_batch)errors.append(np.power(pred_batch - rates, 2))if i % samples_per_batch == 0:train_err = np.sqrt(np.mean(errors))test_err2 = np.array([])for users, items, rates in iter_test:pred_batch = sess.run(infer, feed_dict={user_batch: users, item_batch: items})pred_batch = clip(pred_batch)test_err2 = np.append(test_err2, np.power(pred_batch - rates, 2))end = time.time()print("%02d\t%.3f\t\t%.3f\t\t%.3f secs" % (i // samples_per_batch, train_err, np.sqrt(np.mean(test_err2)), end - start))train_error.append(train_err)val_error.append(np.sqrt(np.mean(test_err2)))start = endsaver.save(sess, checkpoint_prefix)pd.DataFrame({'training error':train_error,'validation error':val_error}).to_pickle("val_error.pkl")print("Training Done !!!")sess.close()
    
  3. 前面的代碼執(zhí)行訓(xùn)練并將誤差保存在 PKL 文件中。最后,它打印了訓(xùn)練和驗(yàn)證誤差以及所花費(fèi)的時(shí)間:

    >>>
    Epoch    Train err    Validation err    Elapsed Time
    00          2.816          2.812                0.118 secs
    01          2.813          2.812                4.898 secs
    …          …               …                     …
    48          2.770          2.767                1.618 secs
    49          2.765          2.760                1.678 secs
    

完成訓(xùn)練!!!

結(jié)果是刪節(jié),只顯示了幾個(gè)步驟。現(xiàn)在讓我們以圖形方式看到這些誤差:

error = pd.read_pickle("val_error.pkl")
error.plot(title="Training vs validation error (per epoch)")
plt.ylabel('Error/loss')
plt.xlabel('Epoch');
plt.show()
>>>

Training the model with the available ratings

圖 5:每個(gè)周期的訓(xùn)練與驗(yàn)證誤差

該圖表明,隨著時(shí)間的推移,訓(xùn)練和驗(yàn)證誤差都會(huì)減少,這意味著我們正朝著正確的方向行走。盡管如此,您仍然可以嘗試增加步驟,看看這兩個(gè)值是否可以進(jìn)一步降低,這意味著更高的準(zhǔn)確率。

使用已保存的模型執(zhí)行推斷

以下代碼使用保存的模型執(zhí)行模型推理,并打印整體驗(yàn)證誤差:

if val:print("Validation ...")init_op = tf.global_variables_initializer()session_conf = tf.ConfigProto(allow_soft_placement=allow_soft_placement,log_device_placement=log_device_placement)with tf.Session(config = session_conf) as sess:new_saver = tf.train.import_meta_graph("{}.meta".format(checkpoint_prefix))new_saver.restore(sess, tf.train.latest_checkpoint(checkpoint_dir))test_err2 = np.array([])for users, items, rates in iter_test:pred_batch = sess.run(infer, feed_dict={user_batch: users, item_batch: items})pred_batch = clip(pred_batch)test_err2 = np.append(test_err2, np.power(pred_batch - rates, 2))print("Validation Error: ",np.sqrt(np.mean(test_err2)))print("Done !!!")
sess.close()
>>>
Validation Error:  2.14626890224
Done!!!

生成用戶項(xiàng)表

以下方法創(chuàng)建用戶項(xiàng)數(shù)據(jù)幀。它用于創(chuàng)建訓(xùn)練有素的DataFrame。使用 SVD 訓(xùn)練模型在此處填寫用戶項(xiàng)表中的所有缺失值。它采用評(píng)級(jí)數(shù)據(jù)幀并存儲(chǔ)所有電影的所有用戶評(píng)級(jí)。最后,它會(huì)生成一個(gè)填充的評(píng)級(jí)數(shù)據(jù)幀,其中行是用戶,列是項(xiàng):

def create_df(ratings_df=readers.read_file(data_file, sep="::")):if os.path.isfile("model/user_item_table.pkl"):df=pd.read_pickle("user_item_table.pkl")else:df = ratings_df.pivot(index = 'user', columns ='item', values = 'rate').fillna(0)df.to_pickle("user_item_table.pkl")df=df.Tusers=[]items=[]start = time.time()print("Start creating user-item dense table")total_movies=list(ratings_df.item.unique())for index in df.columns.tolist():#rated_movies=ratings_df[ratings_df['user']==index].drop(['st', 'user'], axis=1)rated_movie=[]rated_movie=list(ratings_df[ratings_df['user']==index].drop(['st', 'user'], axis=1)['item'].values)unseen_movies=[]unseen_movies=list(set(total_movies) - set(rated_movie))for movie in unseen_movies:users.append(index)items.append(movie)end = time.time()print(("Found in %.2f seconds" % (end-start)))del dfrated_list = []init_op = tf.global_variables_initializer()session_conf = tf.ConfigProto(allow_soft_placement=allow_soft_placement,log_device_placement=log_device_placement)with tf.Session(config = session_conf) as sess:#sess.run(init_op)print("prediction started ...")new_saver = tf.train.import_meta_graph("{}.meta".format(checkpoint_prefix))new_saver.restore(sess, tf.train.latest_checkpoint(checkpoint_dir))test_err2 = np.array([])rated_list = sess.run(infer, feed_dict={user_batch: users, item_batch: items})rated_list = clip(rated_list)print("Done !!!")sess.close()df_dict={'user':users,'item':items,'rate':rated_list}df = ratings_df.drop(['st'],axis=1).append(pd.DataFrame(df_dict)).pivot(index = 'user', columns ='item', values = 'rate').fillna(0)df.to_pickle("user_item_table_train.pkl")return df

現(xiàn)在讓我們調(diào)用前面的方法來(lái)生成用戶項(xiàng)表作為 pandas 數(shù)據(jù)幀:

create_df(ratings_df = readers.read_file(data_file, sep="::"))

此行將為訓(xùn)練集創(chuàng)建用戶與項(xiàng)目表,并將數(shù)據(jù)幀保存為指定目錄中的user_item_table_train.pkl文件。

聚類類似的電影

對(duì)于這一部分,請(qǐng)參閱kmean.py腳本。此腳本將評(píng)級(jí)數(shù)據(jù)文件作為輸入,并返回電影及其各自的簇。

從技術(shù)上講,本節(jié)的目的是找到類似的電影;例如,用戶 1 喜歡電影 1,并且因?yàn)殡娪?1 和電影 2 相似,所以用戶想要電影 2.讓我們開(kāi)始導(dǎo)入所需的包和模塊:

import tensorflow as tf 
import numpy as np
import pandas as pd
import time
import readers
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.decomposition import PCA

現(xiàn)在讓我們定義要使用的數(shù)據(jù)參數(shù):評(píng)級(jí)數(shù)據(jù)文件的路徑,簇的數(shù)量,K 和最大迭代次數(shù)。此外,我們還定義了是否要使用經(jīng)過(guò)訓(xùn)練的用戶與項(xiàng)目矩陣:

data_file = "Input/ratings.dat" #Data source for the positive dataK = 5 # Number of clustersMAX_ITERS =1000 # Maximum number of iterationsTRAINED = False # Use TRAINED user vs item matrix

然后定義k_mean_clustering ()方法。它返回電影及其各自的群集。它采用評(píng)級(jí)數(shù)據(jù)集ratings_df,這是一個(gè)評(píng)級(jí)數(shù)據(jù)幀。然后它存儲(chǔ)各個(gè)電影的所有用戶評(píng)級(jí),K是簇的數(shù)量,MAX_ITERS是推薦的最大數(shù)量,TRAINED是一種布爾類型,表示是否使用受過(guò)訓(xùn)練的用戶與電影表或未經(jīng)訓(xùn)練的人。

提示

如何找到最佳 K 值

在這里,我們樸素地設(shè)定 K 的值。但是,為了微調(diào)聚類表現(xiàn),我們可以使用一種稱為 Elbow 方法的啟發(fā)式方法。我們從K = 2開(kāi)始,然后,我們通過(guò)增加 K 來(lái)運(yùn)行 K 均值算法并使用 WCSS 觀察成本函數(shù)(CF)的值。在某些時(shí)候,CF 會(huì)大幅下降。然而,隨著 K 值的增加,這種改善變得微不足道??傊?#xff0c;我們可以在 WCSS 的最后一次大跌之后選擇 K 作為最佳值。

最后,k_mean_clustering()函數(shù)返回一個(gè)電影/項(xiàng)目列表和一個(gè)簇列表:

def k_mean_clustering(ratings_df,K,MAX_ITERS,TRAINED=False):if TRAINED:df=pd.read_pickle("user_item_table_train.pkl")else:df=pd.read_pickle("user_item_table.pkl")df = df.Tstart = time.time()N=df.shape[0]points = tf.Variable(df.as_matrix())cluster_assignments = tf.Variable(tf.zeros([N], dtype=tf.int64))centroids = tf.Variable(tf.slice(points.initialized_value(), [0,0], [K,df.shape[1]]))rep_centroids = tf.reshape(tf.tile(centroids, [N, 1]), [N, K, df.shape[1]])rep_points = tf.reshape(tf.tile(points, [1, K]), [N, K, df.shape[1]])sum_squares = tf.reduce_sum(tf.square(rep_points - rep_centroids),reduction_indices=2)best_centroids = tf.argmin(sum_squares, 1)    did_assignments_change = tf.reduce_any(tf.not_equal(best_centroids, cluster_assignments))means = bucket_mean(points, best_centroids, K)with tf.control_dependencies([did_assignments_change]):do_updates = tf.group(centroids.assign(means),cluster_assignments.assign(best_centroids))init = tf.global_variables_initializer()sess = tf.Session()sess.run(init)changed = Trueiters = 0while changed and iters < MAX_ITERS:iters += 1[changed, _] = sess.run([did_assignments_change, do_updates])[centers, assignments] = sess.run([centroids, cluster_assignments])end = time.time()print (("Found in %.2f seconds" % (end-start)), iters, "iterations")cluster_df=pd.DataFrame({'movies':df.index.values,'clusters':assignments})cluster_df.to_csv("clusters.csv",index=True)return assignments,df.index.values

在前面的代碼中,我們有一個(gè)愚蠢的初始化,在某種意義上我們使用前 K 個(gè)點(diǎn)作為起始質(zhì)心。在現(xiàn)實(shí)世界中,它可以進(jìn)一步改進(jìn)。

在前面的代碼塊中,我們復(fù)制每個(gè)質(zhì)心的 N 個(gè)副本和每個(gè)數(shù)據(jù)點(diǎn)的 K 個(gè)副本。然后我們減去并計(jì)算平方距離的總和。然后我們使用argmin選擇最低距離點(diǎn)。但是,在計(jì)算分配是否已更改之前,我們不會(huì)編寫已分配的群集變量,因此具有依賴性。

如果仔細(xì)查看前面的代碼,有一個(gè)名為bucket_mean()的函數(shù)。它獲取數(shù)據(jù)點(diǎn),最佳質(zhì)心和暫定簇的數(shù)量 K,并計(jì)算在簇計(jì)算中使用的平均值:

def bucket_mean(data, bucket_ids, num_buckets):total = tf.unsorted_segment_sum(data, bucket_ids, num_buckets)count = tf.unsorted_segment_sum(tf.ones_like(data), bucket_ids, num_buckets)return total / count

一旦我們訓(xùn)練了我們的 K 均值模型,下一個(gè)任務(wù)就是可視化代表類似電影的那些簇。為此,我們有一個(gè)名為showClusters()的函數(shù),它接受用戶項(xiàng)表,CSV 文件(clusters.csv)中寫入的聚簇?cái)?shù)據(jù),主成分?jǐn)?shù)(默認(rèn)為 2)和 SVD 求解器(可能的值是隨機(jī)的和完整的)。

問(wèn)題是,在 2D 空間中,很難繪制代表電影簇的所有數(shù)據(jù)點(diǎn)。出于這個(gè)原因,我們應(yīng)用主成分分析(PCA)來(lái)降低維數(shù)而不會(huì)犧牲質(zhì)量:

    user_item=pd.read_pickle(user_item_table)cluster=pd.read_csv(clustered_data, index_col=False)user_item=user_item.Tpcs = PCA(number_of_PCA_components, svd_solver) cluster['x']=pcs.fit_transform(user_item)[:,0]cluster['y']=pcs.fit_transform(user_item)[:,1]fig = plt.figure()ax = plt.subplot(111)ax.scatter(cluster[cluster['clusters']==0]['x'].values,cluster[cluster['clusters']==0]['y'].values,color="r", label='cluster 0')ax.scatter(cluster[cluster['clusters']==1]['x'].values,cluster[cluster['clusters']==1]['y'].values,color="g", label='cluster 1')ax.scatter(cluster[cluster['clusters']==2]['x'].values,cluster[cluster['clusters']==2]['y'].values,color="b", label='cluster 2')ax.scatter(cluster[cluster['clusters']==3]['x'].values,cluster[cluster['clusters']==3]['y'].values,color="k", label='cluster 3')ax.scatter(cluster[cluster['clusters']==4]['x'].values,cluster[cluster['clusters']==4]['y'].values,color="c", label='cluster 4')ax.legend()plt.title("Clusters of similar movies using K-means")plt.ylabel('PC2')plt.xlabel('PC1');plt.show()

做得好。我們將評(píng)估我們的模型并在評(píng)估步驟中繪制簇。

預(yù)測(cè)用戶的電影評(píng)級(jí)

為此,我編寫了一個(gè)名為prediction()的函數(shù)。它采用有關(guān)用戶和項(xiàng)目(在本例中為電影)的示例輸入,并按名稱從圖創(chuàng)建 TensorFlow 占位符。然后它求值這些張量。在以下代碼中,需要注意的是 TensorFlow 假定檢查點(diǎn)目錄已存在,因此請(qǐng)確保它已存在。有關(guān)此步驟的詳細(xì)信息,請(qǐng)參閱run.py文件。請(qǐng)注意,此腳本不顯示任何結(jié)果,但在main.py腳本中進(jìn)一步調(diào)用此腳本中名為prediction的函數(shù)進(jìn)行預(yù)測(cè):

def prediction(users=predicted_user, items=predicted_item, allow_soft_placement=allow_soft_placement,\
log_device_placement=log_device_placement, checkpoint_dir=checkpoint_dir):rating_prediction=[]checkpoint_prefix = os.path.join(checkpoint_dir, "model")graph = tf.Graph()with graph.as_default():session_conf = tf.ConfigProto(allow_soft_placement=allow_soft_placement,log_device_placement=log_device_placement)with tf.Session(config = session_conf) as sess:new_saver = tf.train.import_meta_graph("{}.meta".format(checkpoint_prefix))new_saver.restore(sess, tf.train.latest_checkpoint(checkpoint_dir))user_batch = graph.get_operation_by_name("id_user").outputs[0]item_batch = graph.get_operation_by_name("id_item").outputs[0]predictions = graph.get_operation_by_name("svd_inference").outputs[0]pred = sess.run(predictions, feed_dict={user_batch: users, item_batch: items})pred = clip(pred)sess.close()return  pred

我們將看到如何使用此方法來(lái)預(yù)測(cè)電影的前 K 部電影和用戶評(píng)級(jí)。在前面的代碼段中,clip()是一個(gè)用戶定義的函數(shù),用于限制數(shù)組中的值。這是實(shí)現(xiàn):

def clip(x):return np.clip(x, 1.0, 5.0) # rating 1 to 5

現(xiàn)在讓我們看看,我們?nèi)绾问褂?code>prediction()方法來(lái)制作用戶的一組電影評(píng)級(jí)預(yù)測(cè):

def user_rating(users,movies):if type(users) is not list: users=np.array([users])if type(movies) is not list:movies=np.array([movies])return prediction(users,movies)

上述函數(shù)返回各個(gè)用戶的用戶評(píng)級(jí)。它采用一個(gè)或多個(gè)數(shù)字的列表,一個(gè)或多個(gè)用戶 ID 的列表,以及一個(gè)或多個(gè)數(shù)字的列表以及一個(gè)或多個(gè)電影 ID 的列表。最后,它返回預(yù)測(cè)電影列表。

尋找前 K 部電影

以下方法提取用戶未見(jiàn)過(guò)的前 K 個(gè)電影,其中 K 是任意整數(shù),例如 10.函數(shù)的名稱是top_k_movies()。它返回特定用戶的前 K 部電影。它需要一個(gè)用戶 ID 列表和評(píng)級(jí)數(shù)據(jù)幀。然后它存儲(chǔ)這些電影的所有用戶評(píng)級(jí)。輸出是包含用戶 ID 作為鍵的字典,以及該用戶的前 K 電影列表作為值:

def top_k_movies(users,ratings_df,k):dicts={}if type(users) is not list:users = [users]for user in users:rated_movies = ratings_df[ratings_df['user']==user].drop(['st', 'user'], axis=1)rated_movie = list(rated_movies['item'].values)total_movies = list(ratings_df.item.unique())unseen_movies = list(set(total_movies) - set(rated_movie))rated_list = []rated_list = prediction(np.full(len(unseen_movies),user),np.array(unseen_movies))useen_movies_df = pd.DataFrame({'item': unseen_movies,'rate':rated_list})top_k = list(useen_movies_df.sort_values(['rate','item'], ascending=[0, 0])['item'].head(k).values)dicts.update({user:top_k})result = pd.DataFrame(dicts)result.to_csv("user_top_k.csv")return dicts

在前面的代碼段中,prediction()是我們之前描述的用戶定義函數(shù)。我們將看到一個(gè)如何預(yù)測(cè)前 K 部電影的例子(更多或見(jiàn)后面的部分見(jiàn)Test.py)。

預(yù)測(cè)前 K 類似的電影

我編寫了一個(gè)名為top_k_similar_items()的函數(shù),它計(jì)算并返回與特定電影類似的 K 個(gè)電影。它需要一個(gè)數(shù)字列表,數(shù)字,電影 ID 列表和評(píng)級(jí)數(shù)據(jù)幀。它存儲(chǔ)這些電影的所有用戶評(píng)級(jí)。它還將 K 作為自然數(shù)。

TRAINED的值可以是TRUEFALSE,它指定是使用受過(guò)訓(xùn)練的用戶還是使用電影表或未經(jīng)訓(xùn)練的用戶。最后,它返回一個(gè) K 電影列表,類似于作為輸入傳遞的電影:

def top_k_similar_items(movies,ratings_df,k,TRAINED=False):if TRAINED:df=pd.read_pickle("user_item_table_train.pkl")else:df=pd.read_pickle("user_item_table.pkl")corr_matrix=item_item_correlation(df,TRAINED)if type(movies) is not list:return corr_matrix[movies].sort_values(ascending=False).drop(movies).index.values[0:k]else:dict={}for movie in movies:           dict.update({movie:corr_matrix[movie].sort_values(ascending=False).drop(movie).index.values[0:k]})pd.DataFrame(dict).to_csv("movie_top_k.csv")return dict

在前面的代碼中,item_item_correlation()函數(shù)是一個(gè)用戶定義的函數(shù),它計(jì)算在預(yù)測(cè)前 K 個(gè)類似電影時(shí)使用的電影 - 電影相關(guān)性。方法如下:

def item_item_correlation(df,TRAINED):if TRAINED:if os.path.isfile("model/item_item_corr_train.pkl"):df_corr=pd.read_pickle("item_item_corr_train.pkl")else:df_corr=df.corr()df_corr.to_pickle("item_item_corr_train.pkl")else:if os.path.isfile("model/item_item_corr.pkl"):df_corr=pd.read_pickle("item_item_corr.pkl")else:df_corr=df.corr()df_corr.to_pickle("item_item_corr.pkl")return df_corr

計(jì)算用戶 - 用戶相似度

為了計(jì)算用戶 - 用戶相似度,我編寫了user_similarity()函數(shù),它返回兩個(gè)用戶之間的相似度。它需要三個(gè)參數(shù):用戶 1,用戶 2;評(píng)級(jí)數(shù)據(jù)幀;并且TRAINED的值可以是TRUEFALSE,并且指的是是否應(yīng)該使用受過(guò)訓(xùn)練的用戶與電影表或未經(jīng)訓(xùn)練的用戶。最后,它計(jì)算用戶之間的 Pearson 系數(shù)(介于 -1 和 1 之間的值):

def user_similarity(user_1,user_2,ratings_df,TRAINED=False):corr_matrix=user_user_pearson_corr(ratings_df,TRAINED)return corr_matrix[user_1][user_2]

在前面的函數(shù)中,user_user_pearson_corr()是一個(gè)計(jì)算用戶 - 用戶 Pearson 相關(guān)性的函數(shù):

def user_user_pearson_corr(ratings_df,TRAINED):if TRAINED:if os.path.isfile("model/user_user_corr_train.pkl"):df_corr=pd.read_pickle("user_user_corr_train.pkl")else:df =pd.read_pickle("user_item_table_train.pkl")df=df.Tdf_corr=df.corr()df_corr.to_pickle("user_user_corr_train.pkl")else:if os.path.isfile("model/user_user_corr.pkl"):df_corr=pd.read_pickle("user_user_corr.pkl")else:df = pd.read_pickle("user_item_table.pkl")df=df.Tdf_corr=df.corr()df_corr.to_pickle("user_user_corr.pkl")return df_corr

評(píng)估推薦系統(tǒng)

在這個(gè)小節(jié)中,我們將通過(guò)繪制它們以評(píng)估電影如何在不同的簇中傳播來(lái)評(píng)估簇。

然后,我們將看到前 K 部電影,并查看我們之前討論過(guò)的用戶 - 用戶相似度和其他指標(biāo)?,F(xiàn)在讓我們開(kāi)始導(dǎo)入所需的庫(kù):

import tensorflow as tf
import pandas as pd
import readers
import main
import kmean as km
import numpy as np

接下來(lái),讓我們定義用于評(píng)估的數(shù)據(jù)參數(shù):

DATA_FILE = "Input/ratings.dat" # Data source for the positive data.
K = 5 #Number of clusters
MAX_ITERS = 1000 #Maximum number of iterations
TRAINED = False # Use TRAINED user vs item matrix
USER_ITEM_TABLE = "user_item_table.pkl" 
COMPUTED_CLUSTER_CSV = "clusters.csv" 
NO_OF_PCA_COMPONENTS = 2 #number of pca components
SVD_SOLVER = "randomized" #svd solver -e.g. randomized, full etc.

讓我們看看加載將在k_mean_clustering()方法的調(diào)用調(diào)用中使用的評(píng)級(jí)數(shù)據(jù)集:

ratings_df = readers.read_file("Input/ratings.dat", sep="::")
clusters,movies = km.k_mean_clustering(ratings_df, K, MAX_ITERS, TRAINED = False)
cluster_df=pd.DataFrame({'movies':movies,'clusters':clusters})

做得好!現(xiàn)在讓我們看一些簡(jiǎn)單的輸入簇(電影和各自的簇):

print(cluster_df.head(10))
>>>
clusters  movies
0         0       0
1         4       1
2         4       2
3         3       3
4         4       4
5         2       5
6         4       6
7         3       7
8         3       8
9         2       9
print(cluster_df[cluster_df['movies']==1721])
>>>clusters  movies
1575         2    1721
print(cluster_df[cluster_df['movies']==647])
>>>
clusters  movies
627         2     647

讓我們看看電影是如何分散在群集中的:

km.showClusters(USER_ITEM_TABLE, COMPUTED_CLUSTER_CSV, NO_OF_PCA_COMPONENTS, SVD_SOLVER)
>>>

Evaluating the recommender system

圖 6:類似電影的簇

如果我們查看該圖,很明顯數(shù)據(jù)點(diǎn)更準(zhǔn)確地聚集在簇 3 和 4 上。但是,簇 0,1 和 2 更分散并且不能很好地聚類。

在這里,我們沒(méi)有計(jì)算任何準(zhǔn)確率指標(biāo),因?yàn)橛?xùn)練數(shù)據(jù)沒(méi)有標(biāo)簽?,F(xiàn)在讓我們?yōu)榻o定的相應(yīng)電影名稱計(jì)算前 K 個(gè)類似的電影并打印出來(lái):

ratings_df = readers.read_file("Input/ratings.dat", sep="::")
topK = main.top_k_similar_items(9,ratings_df = ratings_df,k = 10,TRAINED = False)
print(topK)
>>>
[1721, 1369, 164, 3081, 732, 348, 647, 2005, 379, 3255]

上述結(jié)果為電影9 ::Sudden Death (1995)::Action計(jì)算了 Top-K 類似的電影。現(xiàn)在,如果您觀察movies.dat文件,您將看到以下電影與此類似:

1721::Titanic (1997)::Drama|Romance
1369::I Can't Sleep (J'ai pas sommeil) (1994)::Drama|Thriller
164::Devil in a Blue Dress (1995)::Crime|Film-Noir|Mystery|Thriller
3081::Sleepy Hollow (1999)::Horror|Romance
732::Original Gangstas (1996)::Crime
348::Bullets Over Broadway (1994)::Comedy
647::Courage Under Fire (1996)::Drama|War
2005::Goonies, The (1985)::Adventure|Children's|Fantasy
379::Timecop (1994)::Action|Sci-Fi
3255::League of Their Own, A (1992)::Comedy|Drama

現(xiàn)在讓我們計(jì)算用戶 - 用戶 Pearson 相關(guān)性。當(dāng)運(yùn)行此用戶相似度函數(shù)時(shí),在第一次運(yùn)行時(shí)需要時(shí)間來(lái)提供輸出但在此之后,其響應(yīng)是實(shí)時(shí)的:

print(main.user_similarity(1,345,ratings_df))
>>>
0.15045477803357316 
Now let's compute the aspect rating given by a user for a movie:
print(main.user_rating(0,1192))
>>>
4.25545645
print(main.user_rating(0,660))
>>>
3.20203304

讓我們看一下用戶的 K 電影推薦:

print(main.top_k_movies([768],ratings_df,10))
>>>
{768: [2857, 2570, 607, 109, 1209, 2027, 592, 588, 2761, 479]}
print(main.top_k_movies(1198,ratings_df,10))
>>>
{1198: [2857, 1195, 259, 607, 109, 2027, 592, 857, 295, 479]}

到目前為止,我們已經(jīng)看到如何使用電影和評(píng)級(jí)數(shù)據(jù)集開(kāi)發(fā)簡(jiǎn)單的 RE。但是,大多數(shù)推薦問(wèn)題都假設(shè)我們有一個(gè)由(用戶,項(xiàng)目,評(píng)級(jí))元組集合形成的消費(fèi)/評(píng)級(jí)數(shù)據(jù)集。這是協(xié)同過(guò)濾算法的大多數(shù)變體的起點(diǎn),并且已經(jīng)證明它們可以產(chǎn)生良好的結(jié)果;但是,在許多應(yīng)用中,我們有大量的項(xiàng)目元數(shù)據(jù)(標(biāo)簽,類別和流派)可用于做出更好的預(yù)測(cè)。

這是將 FM 用于特征豐富的數(shù)據(jù)集的好處之一,因?yàn)橛幸环N自然的方式可以在模型中包含額外的特征,并且可以使用維度參數(shù) d 對(duì)高階交互進(jìn)行建模(參見(jiàn)下面的圖 7)更多細(xì)節(jié))。

最近的一些類型的研究表明,特征豐富的數(shù)據(jù)集可以提供更好的預(yù)測(cè):i)Xiangnan He 和 Tat-Seng Chua,用于稀疏預(yù)測(cè)分析的神經(jīng)分解機(jī)。在 SIGIR '17 的論文集中,2017 年 8 月 7 日至 11 日,日本東京新宿。ii)Jun Xiao,Hao Ye,Xiantian He,Hanwang Zhang,Fei Wu 和 Tat-Seng Chua(2017)Attentional Factorization Machines:Learning the Learning 通過(guò)注意網(wǎng)絡(luò)的特征交互的權(quán)重 IJCAI,墨爾本,澳大利亞,2017 年 8 月 19 - 25 日。

這些論文解釋了如何將現(xiàn)有數(shù)據(jù)轉(zhuǎn)換為特征豐富的數(shù)據(jù)集,以及如何在數(shù)據(jù)集上實(shí)現(xiàn) FM。因此,研究人員正在嘗試使用 FM 來(lái)開(kāi)發(fā)更準(zhǔn)確和更強(qiáng)大的 RE。在下一節(jié)中,我們將看到一些使用 FM 和一些變體的示例。

分解機(jī)和推薦系統(tǒng)

在本節(jié)中,我們將看到兩個(gè)使用 FM 開(kāi)發(fā)更強(qiáng)大的推薦系統(tǒng)的示例 。我們將首先簡(jiǎn)要介紹 FM 及其在冷啟動(dòng)推薦問(wèn)題中的應(yīng)用。

然后我們將看到使用 FM 開(kāi)發(fā)真實(shí)推薦系統(tǒng)的簡(jiǎn)短示例。之后,我們將看到一個(gè)使用稱為神經(jīng)分解機(jī)(NFM)的 FM 算法的改進(jìn)版本的示例。

分解機(jī)

基于 FM 的技術(shù)處于個(gè)性化的前沿。它們已經(jīng)被證明是非常強(qiáng)大的,具有足夠的表達(dá)能力來(lái)推廣現(xiàn)有模型,例如矩陣/張量分解和多項(xiàng)式核回歸。換句話說(shuō),這種類型的算法是監(jiān)督學(xué)習(xí)方法,其通過(guò)結(jié)合矩陣分解算法中不存在的二階特征交互來(lái)增強(qiáng)線性模型的表現(xiàn)。

現(xiàn)有的推薦算法需要(用戶,項(xiàng)目和評(píng)級(jí))元組中的消費(fèi)(產(chǎn)品)或評(píng)級(jí)(電影)數(shù)據(jù)集。這些類型的數(shù)據(jù)集主要用于協(xié)同過(guò)濾(CF)算法的變體。 CF 算法已得到廣泛采用,并已證明可以產(chǎn)生良好的結(jié)果。但是,在許多情況下,我們有大量的項(xiàng)目元數(shù)據(jù)(標(biāo)簽,類別和流派),可以用來(lái)做出更好的預(yù)測(cè)。不幸的是,CF 算法不使用這些類型的元數(shù)據(jù)。

FM 可以使用這些特征豐富的(元)數(shù)據(jù)集。 FM 可以使用這些額外的特征來(lái)模擬指定維度參數(shù) d 的高階交互。最重要的是,FM 還針對(duì)處理大規(guī)模稀疏數(shù)據(jù)集進(jìn)行了優(yōu)化。因此,二階 FM 模型就足夠了,因?yàn)闆](méi)有足夠的信息來(lái)估計(jì)更復(fù)雜的交互:

Factorization machines

圖 7:表示具有特征向量 x 和目標(biāo) y 的個(gè)性化問(wèn)題的示例訓(xùn)練數(shù)據(jù)集。這里的行指的是導(dǎo)演,演員和流派信息的電影和專欄

假設(shè)預(yù)測(cè)問(wèn)題的數(shù)據(jù)集由設(shè)計(jì)矩陣X ∈ R^nxp描述,如圖 7 所示。在圖 1 中,X的第iX ∈ R^p描述了一種情況,其中p是實(shí)數(shù)估值變量。另一方面,y[i]是第i個(gè)情況的預(yù)測(cè)目標(biāo)?;蛘?#xff0c;我們可以將此集合描述為元組(x, y)的集合S,其中(同樣)x ∈ R^p是特征向量,y是其對(duì)應(yīng)的目標(biāo)或標(biāo)簽。

換句話說(shuō),在圖 7 中,每行表示特征向量x[i]與其相應(yīng)的目標(biāo)y[i]。為了便于解釋,這些特征分為活躍用戶(藍(lán)色),活動(dòng)項(xiàng)目(紅色),同一用戶評(píng)級(jí)的其他電影(橙色),月份時(shí)間(綠色)和最后一部電影評(píng)級(jí)指標(biāo)(棕色)。然后,FM 算法使用以下分解的交互參數(shù)來(lái)模擬xp輸入變量之間的所有嵌套交互(直到d階):

Factorization machines

在等式中,v表示與每個(gè)變量(用戶和項(xiàng)目)相關(guān)聯(lián)的 K 維潛在向量,并且括號(hào)運(yùn)算符表示內(nèi)積。具有數(shù)據(jù)矩陣和特征向量的這種表示在許多機(jī)器學(xué)習(xí)方法中是常見(jiàn)的,例如,在線性回歸或支持向量機(jī)(SVM)中。

但是,如果您熟悉矩陣分解(MF)模型,則前面的等式應(yīng)該看起來(lái)很熟悉:它包含全局偏差以及用戶/項(xiàng)目特定的偏差,并包括用戶項(xiàng)目交互?,F(xiàn)在,如果我們假設(shè)每個(gè)x(j)向量在位置ui處僅為非零,我們得到經(jīng)典的 MF 模型:

Factorization machines

然而,用于推薦系統(tǒng)的 MF 模型經(jīng)常遭受冷啟動(dòng)問(wèn)題。我們將在下一節(jié)討論這個(gè)問(wèn)題。

冷啟動(dòng)問(wèn)題和協(xié)同過(guò)濾方法

冷啟動(dòng)這個(gè)問(wèn)題聽(tīng)起來(lái)很有趣,但顧名思義,它源于汽車。假設(shè)你住在阿拉斯加狀態(tài)。由于寒冷,您的汽車發(fā)動(dòng)機(jī)可能無(wú)法順利啟動(dòng),但一旦達(dá)到最佳工作溫度,它將啟動(dòng),運(yùn)行并正常運(yùn)行。

在推薦引擎的領(lǐng)域中,術(shù)語(yǔ)冷啟動(dòng)僅僅意味著對(duì)于引擎來(lái)說(shuō)還不是最佳的環(huán)境以提供最佳結(jié)果。在電子商務(wù)中,冷啟動(dòng)有兩個(gè)不同的類別:產(chǎn)品冷啟動(dòng)和用戶冷啟動(dòng)。

冷啟動(dòng)是基于計(jì)算機(jī)的信息系統(tǒng)中的潛在問(wèn)題,涉及一定程度的自動(dòng)數(shù)據(jù)建模。具體而言,它涉及的問(wèn)題是系統(tǒng)無(wú)法對(duì)尚未收集到足夠信息的用戶或項(xiàng)目進(jìn)行任何推斷。

冷啟動(dòng)問(wèn)題在推薦系統(tǒng)中最為普遍。在協(xié)同過(guò)濾方法中,推薦系統(tǒng)將識(shí)別與活動(dòng)用戶共享偏好的用戶,并提出志同道合的用戶喜歡的項(xiàng)目(并且活躍用戶尚未看到)。由于冷啟動(dòng)問(wèn)題,這種方法將無(wú)法考慮社區(qū)中沒(méi)有人評(píng)定的項(xiàng)目。

通過(guò)在基于內(nèi)容的匹配和協(xié)同過(guò)濾之間采用混合方法,通??梢詼p少冷啟動(dòng)問(wèn)題。尚未收到用戶評(píng)級(jí)的新項(xiàng)目將根據(jù)社區(qū)分配給其他類似項(xiàng)目的評(píng)級(jí)自動(dòng)分配評(píng)級(jí)。項(xiàng)目相似性將根據(jù)項(xiàng)目的基于內(nèi)容的特征來(lái)確定。

使用基于 CF 的方法的推薦引擎根據(jù)用戶操作推薦每個(gè)項(xiàng)目。項(xiàng)目具有的用戶操作越多,就越容易分辨哪個(gè)用戶對(duì)其感興趣以及其他項(xiàng)目與之類似。隨著時(shí)間的推移,系統(tǒng)將能夠提供越來(lái)越準(zhǔn)確的建議。在某個(gè)階段,當(dāng)新項(xiàng)目或用戶添加到用戶項(xiàng)目矩陣時(shí),會(huì)出現(xiàn)此問(wèn)題:

Cold-start problem and collaborative-filtering approaches

圖 8:用戶與項(xiàng)目矩陣有時(shí)會(huì)導(dǎo)致冷啟動(dòng)問(wèn)題

在這種情況下,RE 還沒(méi)有足夠的知識(shí)來(lái)了解這個(gè)新用戶或這個(gè)新項(xiàng)目。類似于 FM 的基于內(nèi)容的過(guò)濾方法是可以結(jié)合以減輕冷啟動(dòng)問(wèn)題的方法。

前兩個(gè)方程之間的主要區(qū)別在于,FM 在潛在向量方面引入了高階相互作用,潛在向量也受分類或標(biāo)簽數(shù)據(jù)的影響。這意味著模型超越了共現(xiàn),以便在每個(gè)特征的潛在表示之間找到更強(qiáng)的關(guān)系。

問(wèn)題的定義和制定

給定用戶在電子商務(wù)網(wǎng)站上的典型會(huì)話期間執(zhí)行的點(diǎn)擊事件的序列 ,目標(biāo)是預(yù)測(cè)用戶是否購(gòu)買或不購(gòu)買,如果他們正在購(gòu)買,他們會(huì)買什么物品。因此,這項(xiàng)任務(wù)可分為兩個(gè)子目標(biāo):

  • 用戶是否會(huì)在此會(huì)話中購(gòu)買物品?
  • 如果是,那么將要購(gòu)買的物品是什么?

為了預(yù)測(cè)在會(huì)話中購(gòu)買的項(xiàng)目的數(shù)量,強(qiáng)大的分類器可以幫助預(yù)測(cè)用戶是否將購(gòu)買該項(xiàng)目的 。在最初實(shí)現(xiàn) FM 后,訓(xùn)練數(shù)據(jù)的結(jié)構(gòu)應(yīng)如下:

Problem definition and formulation

圖 9:用戶與項(xiàng)目/類別/歷史表可用于訓(xùn)練推薦模型

為了準(zhǔn)備這樣的訓(xùn)練集,我們可以使用 pandas 中的get_dummies()方法將所有列轉(zhuǎn)換為分類數(shù)據(jù),因?yàn)?FM 模型使用表示為整數(shù)的分類數(shù)據(jù)。

我們使用兩個(gè)函數(shù)TFFMClassifierTFFMRegressor來(lái)進(jìn)行預(yù)測(cè)(參見(jiàn)items.py)并分別計(jì)算 MSE(參見(jiàn)來(lái)自tffm庫(kù)的quantity.py腳本(在 MIT 許可下))。tffm是基于 TensorFlow 的 FM 和 pandas 實(shí)現(xiàn),用于預(yù)處理和結(jié)構(gòu)化數(shù)據(jù)。這個(gè)基于 TensorFlow 的實(shí)現(xiàn)提供了一個(gè)任意順序(>= 2)分解機(jī),它支持:

  • 密集和稀疏的輸入
  • 不同的(基于梯度的)優(yōu)化方法
  • 通過(guò)不同的損失函數(shù)進(jìn)行分類/回歸(logistic 和 mse 實(shí)現(xiàn))
  • 通過(guò) TensorBoard 記錄

另一個(gè)好處是推理時(shí)間相對(duì)于特征數(shù)量是線性的。

我們要感謝作者并引用他們的工作如下:Mikhail Trofimov,Alexander Novikov,TFFM:TensorFlow 實(shí)現(xiàn)任意順序分解機(jī),GitHub 倉(cāng)庫(kù),2016。

要使用此庫(kù),只需在終端上發(fā)出以下命令:

$ sudo pip3 install tffm # For Python3.x
$ sudo pip install tffm # For Python 2.7.x

在開(kāi)始實(shí)現(xiàn)之前,讓我們看一下我們將在本例中使用的數(shù)據(jù)集。

數(shù)據(jù)集描述

例如,我將使用 RecSys 2015 挑戰(zhàn)數(shù)據(jù)集來(lái)說(shuō)明如何擬合 FM 模型以獲得個(gè)性化推薦。該數(shù)據(jù)包含電子商務(wù)網(wǎng)站的點(diǎn)擊和購(gòu)買事件,以及其他項(xiàng)目類別數(shù)據(jù)。數(shù)據(jù)集的大小約為 275MB,可以從此鏈接下載。

有三個(gè)文件和一個(gè)自述文件;但是,我們將使用youchoose-buys.dat(購(gòu)買活動(dòng))和youchoose-clicks.dat(點(diǎn)擊活動(dòng)):

  • youchoose-clicks.dat:文件中的每條記錄/行都包含以下字段:
    • 會(huì)話 ID:一個(gè)會(huì)話中的一次或多次點(diǎn)擊
    • 時(shí)間戳:發(fā)生點(diǎn)擊的時(shí)間
    • 項(xiàng)目 ID:項(xiàng)目的唯一標(biāo)識(shí)符
    • 類別:項(xiàng)目的類別
  • youchoose-buys.dat:文件中的每條記錄/行都包含以下字段:
    • 會(huì)話 ID:會(huì)話 ID:會(huì)話中的一個(gè)或多個(gè)購(gòu)買事件
    • 時(shí)間戳:購(gòu)買發(fā)生的時(shí)間
    • 物料 ID:物品的唯一標(biāo)識(shí)符
    • 價(jià)格:商品的價(jià)格
    • 數(shù)量:購(gòu)買了多少件商品

youchoose-buys.dat中的會(huì)話 ID 也存在于youchoose-clicks.dat文件中。這意味著具有相同會(huì)話 ID 的記錄一起形成會(huì)話期間某個(gè)用戶的點(diǎn)擊事件序列。

會(huì)話可能很短(幾分鐘)或很長(zhǎng)(幾個(gè)小時(shí)),可能只需點(diǎn)擊一下或點(diǎn)擊幾百次。這一切都取決于用戶的活動(dòng)。

實(shí)現(xiàn)工作流程

讓我們開(kāi)發(fā)一個(gè)預(yù)測(cè)并生成solution.data文件的推薦模型。這是一個(gè)簡(jiǎn)短的工作流程:

  1. 下載并加載 RecSys 2015 挑戰(zhàn)數(shù)據(jù)集,并復(fù)制到本章代碼庫(kù)的data文件夾中

  2. 購(gòu)買數(shù)據(jù)包含會(huì)話 ID,時(shí)間戳,項(xiàng)目 ID,類別和數(shù)量。此外,youchoose-clicks.dat包含會(huì)話 ID,時(shí)間戳,項(xiàng)目 ID 和類別。我們不會(huì)在這里使用時(shí)間戳。我們刪除時(shí)間戳,對(duì)所有列進(jìn)行單熱編碼, 合并買入和點(diǎn)擊數(shù)據(jù)集以使數(shù)據(jù)集特征豐富。在預(yù)處理之后,數(shù)據(jù)看起來(lái)類似于圖 11 中所示的數(shù)據(jù)。

  3. 為簡(jiǎn)化起見(jiàn),我們僅考慮前 10,000 個(gè)會(huì)話,并將數(shù)據(jù)集拆分為訓(xùn)練(75%)和測(cè)試(25%)集。

  4. 然后,我們將測(cè)試分為正常(保留歷史數(shù)據(jù))和冷啟動(dòng)(通過(guò)刪除歷史數(shù)據(jù)),以區(qū)分具有歷史記錄或沒(méi)有歷史記錄的用戶/項(xiàng)目的模型。

  5. 然后我們使用tffm訓(xùn)練我們的 FM 模型,這是 TensorFlow 中 FM 的實(shí)現(xiàn),并使用訓(xùn)練數(shù)據(jù)訓(xùn)練模型。

  6. 最后,我們?cè)谡:屠鋯?dòng)數(shù)據(jù)集上評(píng)估模型。

    Workflow of the implementation

    圖 10:使用 FM 預(yù)測(cè)會(huì)話中已購(gòu)買項(xiàng)目列表的工作流程

預(yù)處理

如果我們想充分利用類別和擴(kuò)展的歷史數(shù)據(jù),我們需要加載數(shù)據(jù)并將其轉(zhuǎn)換為正確的格式。因此,在準(zhǔn)備訓(xùn)練集之前,必須進(jìn)行一些預(yù)處理。讓我們從加載包和模塊開(kāi)始:

import tensorflow as tf
import pandas as pd
from collections import Counter
from tffm import TFFMClassifier
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.metrics import accuracy_score
import os

我是 ,假設(shè)您已經(jīng)從前面提到的鏈接下載了數(shù)據(jù)集?,F(xiàn)在讓我們加載數(shù)據(jù)集:

buys = open('data/yoochoose-buys.dat', 'r')
clicks = open('data/yoochoose-clicks.dat', 'r')

現(xiàn)在為點(diǎn)擊創(chuàng)建 pandas 數(shù)據(jù)幀并購(gòu)買數(shù)據(jù)集:

initial_buys_df = pd.read_csv(buys, names=['Session ID', 'Timestamp', 'Item ID', 'Category', 'Quantity'], dtype={'Session ID': 'float32', 'Timestamp': 'str', 'Item ID': 'float32','Category': 'str'})
initial_buys_df.set_index('Session ID', inplace=True)
initial_clicks_df = pd.read_csv(clicks, names=['Session ID', 'Timestamp', 'Item ID', 'Category'],dtype={'Category': 'str'})
initial_clicks_df.set_index('Session ID', inplace=True)

我們不需要在這個(gè)例子中使用時(shí)間戳,所以讓我們從數(shù)據(jù)幀中刪除它們:

initial_buys_df = initial_buys_df.drop('Timestamp', 1)print(initial_buys_df.head())  # first five recordsprint(initial_buys_df.shape)   # shape of the dataframe
>>>

Preprocessing

initial_clicks_df = initial_clicks_df.drop('Timestamp', 1)
print(initial_clicks_df.head())
print(initial_clicks_df.shape)
>>>

Preprocessing

由于在此示例中我們不使用時(shí)間戳,因此從數(shù)據(jù)幀(df)中刪除Timestamp列:

initial_buys_df = initial_buys_df.drop('Timestamp', 1)
print(initial_buys_df.head(n=5))
print(initial_buys_df.shape)
>>>

Preprocessing

initial_clicks_df = initial_clicks_df.drop('Timestamp', 1)
print(initial_clicks_df.head(n=5))
print(initial_clicks_df.shape)
>>>

Preprocessing

讓我們選取前 10,000 名購(gòu)買用戶:

x = Counter(initial_buys_df.index).most_common(10000)
top_k = dict(x).keys()
initial_buys_df = initial_buys_df[initial_buys_df.index.isin(top_k)]print(initial_buys_df.head())print(initial_buys_df.shape)
>>>

Preprocessing

initial_clicks_df = initial_clicks_df[initial_clicks_df.index.isin(top_k)]print(initial_clicks_df.head())print(initial_clicks_df.shape)
>>>

Preprocessing

現(xiàn)在讓我們創(chuàng)建索引的副本,因?yàn)槲覀冞€將對(duì)其應(yīng)用單熱編碼:

initial_buys_df['_Session ID'] = initial_buys_df.index
print(initial_buys_df.head())
print(initial_buys_df.shape)
>>>

Preprocessing

正如我們之前提到的 ,我們可以將歷史參與數(shù)據(jù)引入我們的 FM 模型。我們將使用一些group_by魔法生成整個(gè)用戶參與的歷史記錄。首先,我們對(duì)所有列進(jìn)行單熱編碼以獲得點(diǎn)擊和購(gòu)買:

transformed_buys = pd.get_dummies(initial_buys_df)print(transformed_buys.shape)
>>>
(106956, 356)
transformed_clicks = pd.get_dummies(initial_clicks_df)print(transformed_clicks.shape)
>>>
(209024, 56)

現(xiàn)在是時(shí)候匯總項(xiàng)目和類別的歷史數(shù)據(jù)了:

filtered_buys = transformed_buys.filter(regex="Item.*|Category.*")print(filtered_buys.shape)
>>>
(106956, 354)
filtered_clicks = transformed_clicks.filter(regex="Item.*|Category.*")print(filtered_clicks.shape)
>>>
(209024, 56)
historical_buy_data = filtered_buys.groupby(filtered_buys.index).sum()print(historical_buy_data.shape)
>>>
(10000, 354)
historical_buy_data = historical_buy_data.rename(columns=lambda column_name: 'buy history:' + column_name)print(historical_buy_data.shape)>>>(10000, 354)
historical_click_data = filtered_clicks.groupby(filtered_clicks.index).sum()print(historical_click_data.shape)>>>
(10000, 56)
historical_click_data = historical_click_data.rename(columns=lambda column_name: 'click history:' + column_name)

然后我們合并每個(gè)user_id的歷史數(shù)據(jù):

merged1 = pd.merge(transformed_buys, historical_buy_data, left_index=True, right_index=True)
print(merged1.shape)
merged2 = pd.merge(merged1, historical_click_data, left_index=True, right_index=True)
print(merged2.shape)
>>>
(106956, 710)
(106956, 766)

然后我們將數(shù)量作為目標(biāo)并將其轉(zhuǎn)換為二元:

y = np.array(merged2['Quantity'].as_matrix())

現(xiàn)在讓我們將y轉(zhuǎn)換為二元:如果購(gòu)買發(fā)生,為1;否則為0

for i in range(y.shape[0]):if y[i]!=0:y[i]=1else:y[i]=0
print(y.shape)
print(y[0:100])
print(y, y.shape[0])
print(y[0])
print(y[0:100])
print(y, y.shape)
>>>

Preprocessing

訓(xùn)練 FM 模型

由于我們準(zhǔn)備了數(shù)據(jù)集,下一個(gè)任務(wù)是創(chuàng)建 MF 模型。首先,讓我們將數(shù)據(jù)分成訓(xùn)練和測(cè)試集:

X_tr, X_te, y_tr, y_te = train_test_split(merged2, y, test_size=0.25)

然后我們將測(cè)試數(shù)據(jù)分成一半,一個(gè)用于正常測(cè)試,一個(gè)用于冷啟動(dòng)測(cè)試:

X_te, X_te_cs, y_te, y_te_cs = train_test_split(X_te, y_te, test_size=0.5)

現(xiàn)在讓我們?cè)跀?shù)據(jù)幀中包含會(huì)話 ID 和項(xiàng)目 ID:

test_x = pd.DataFrame(X_te, columns = ['Item ID'])
print(test_x.head())
>>>

Training the FM model

test_x_cs = pd.DataFrame(X_te_cs, columns = ['Item ID'])
print(test_x_cs.head())
>>>

Training the FM model

然后我們從數(shù)據(jù)集中刪除不需要的特征:

X_tr.drop(['Item ID', '_Session ID', 'click history:Item ID', 'buy history:Item ID', 'Quantity'], 1, inplace=True)
X_te.drop(['Item ID', '_Session ID', 'click history:Item ID', 'buy history:Item ID', 'Quantity'], 1, inplace=True)
X_te_cs.drop(['Item ID', '_Session ID', 'click history:Item ID', 'buy history:Item ID', 'Quantity'], 1, inplace=True)

然后我們需要將DataFrame轉(zhuǎn)換為數(shù)組:

ax_tr = np.array(X_tr)
ax_te = np.array(X_te)
ax_te_cs = np.array(X_te_cs)

既然 pandas DataFrame已經(jīng)轉(zhuǎn)換為 NumPy 數(shù)組,我們需要做一些null處理。我們簡(jiǎn)單地用零替換 NaN:

ax_tr = np.nan_to_num(ax_tr)
ax_te = np.nan_to_num(ax_te)
ax_te_cs = np.nan_to_num(ax_te_cs)

然后我們用優(yōu)化的超參數(shù)實(shí)例化 TF 模型進(jìn)行分類:

model = TFFMClassifier(order=2, rank=7, optimizer=tf.train.AdamOptimizer(learning_rate=0.001), n_epochs=100, batch_size=1024,init_std=0.001,reg=0.01,input_type='dense',log_dir = ' logs/',verbose=1,seed=12345)

在我們開(kāi)始訓(xùn)練模型之前,我們必須為冷啟動(dòng)準(zhǔn)備數(shù)據(jù):

cold_start = pd.DataFrame(ax_te_cs, columns=X_tr.columns)

和前面提到的一樣,如果我們只能訪問(wèn)類別而沒(méi)有歷史點(diǎn)擊/購(gòu)買數(shù)據(jù),我們也有興趣了解會(huì)發(fā)生什么。讓我們刪除cold_start測(cè)試集的歷史點(diǎn)擊和購(gòu)買數(shù)據(jù):

for column in cold_start.columns:if ('buy' in column or 'click' in column) and ('Category' not in column):cold_start[column] = 0

現(xiàn)在讓我們訓(xùn)練模型:

model.fit(ax_tr, y_tr, show_progress=True)

其中一項(xiàng)最重要的任務(wù)是預(yù)測(cè)會(huì)議中的購(gòu)買事件:

predictions = model.predict(ax_te)
print('accuracy: {}'.format(accuracy_score(y_te, predictions)))
print("predictions:",predictions[:10])
print("actual value:",y_te[:10])
>>>
accuracy: 1.0
predictions: [0 0 1 0 0 1 0 1 1 0]
actual value: [0 0 1 0 0 1 0 1 1 0]cold_start_predictions = model.predict(ax_te_cs)
print('Cold-start accuracy: {}'.format(accuracy_score(y_te_cs, cold_start_predictions)))
print("cold start predictions:",cold_start_predictions[:10])
print("actual value:",y_te_cs[:10])
>>>
Cold-start accuracy: 1.0
cold start predictions: [1 1 1 1 1 0 1 0 0 1]
actual value: [1 1 1 1 1 0 1 0 0 1]

然后讓我們將預(yù)測(cè)值添加到測(cè)試數(shù)據(jù)中:

test_x["Predicted"] = predictions
test_x_cs["Predicted"] = cold_start_predictions

現(xiàn)在是時(shí)候找到測(cè)試數(shù)據(jù)中每個(gè)session_id的所有買入事件并檢索相應(yīng)的項(xiàng)目 ID:

sess = list(set(test_x.index))
fout = open("solution.dat", "w")
print("writing the results into .dat file....")
for i in sess:if test_x.loc[i]["Predicted"].any()!= 0:fout.write(str(i)+";"+','.join(s for s in str(test_x.loc[i]["Item ID"].tolist()).strip('[]').split(','))+'\n')
fout.close()
>>>
writing the results into .dat file....

然后我們對(duì)冷啟動(dòng)測(cè)試數(shù)據(jù)做同樣的事情:

sess_cs = list(set(test_x_cs.index))
fout = open("solution_cs.dat", "w")
print("writing the cold start results into .dat file....")
for i in sess_cs:if test_x_cs.loc[i]["Predicted"].any()!= 0:fout.write(str(i)+";"+','.join(s for s in str(test_x_cs.loc[i]["Item ID"].tolist()).strip('[]').split(','))+'\n')
fout.close()
>>>
writing the cold start results into .dat file....
print("completed..!!")
>>>
completed!!

最后,我們銷毀模型以釋放內(nèi)存:

model.destroy()

另外,我們可以看到文件的示例內(nèi)容:

11009963;214853767
10846132;214854343, 214851590
8486841;214848315
10256314;214854125
8912828;214853085
11304897;214567215
9928686;214854300, 214819577
10125303;214567215, 214853852
10223609;214854358

考慮到我們使用相對(duì)較小的數(shù)據(jù)集來(lái)擬合我們的模型, 實(shí)驗(yàn)結(jié)果很好。正如預(yù)期的那樣,如果我們可以通過(guò)項(xiàng)目購(gòu)買和點(diǎn)擊訪問(wèn)所有信息集,則更容易生成預(yù)測(cè),但我們?nèi)匀恢皇褂脜R總類別數(shù)據(jù)獲得冷啟動(dòng)建議的預(yù)測(cè)。

既然我們已經(jīng)看到客戶將在每個(gè)會(huì)話中購(gòu)買,那么計(jì)算兩個(gè)測(cè)試集的均方誤差將會(huì)很棒。TFFMRegressor方法可以幫助我們解決這個(gè)問(wèn)題。為此,請(qǐng)使用quantity.py腳本。

首先,問(wèn)題是如果我們只能訪問(wèn)類別而沒(méi)有歷史點(diǎn)擊/購(gòu)買數(shù)據(jù)會(huì)發(fā)生什么。讓我們刪除cold_start測(cè)試集的歷史點(diǎn)擊和購(gòu)買數(shù)據(jù):

for column in cold_start.columns:if ('buy' in column or 'click' in column) and ('Category' not in column):cold_start[column] = 0

讓我們創(chuàng)建 MF 模型。您可以使用超參數(shù):

reg_model = TFFMRegressor(order=2,rank=7,optimizer=tf.train.AdamOptimizer(learning_rate=0.1),n_epochs=100,batch_size=-1,init_std=0.001,input_type='dense',log_dir = ' logs/',verbose=1)

在前面的代碼塊中,隨意放入您自己的日志記錄路徑?,F(xiàn)在是時(shí)候使用正常和冷啟動(dòng)訓(xùn)練集訓(xùn)練回歸模型:

reg_model.fit(X_tr, y_tr, show_progress=True)

然后我們計(jì)算兩個(gè)測(cè)試集的均方誤差:

predictions = reg_model.predict(X_te)
print('MSE: {}'.format(mean_squared_error(y_te, predictions)))
print("predictions:",predictions[:10])
print("actual value:",y_te[:10])
cold_start_predictions = reg_model.predict(X_te_cs)
print('Cold-start MSE: {}'.format(mean_squared_error(y_te_cs, cold_start_predictions)))
print("cold start predictions:",cold_start_predictions[:10])
print("actual value:",y_te_cs[:10])
print("Regression completed..!!")
>>>MSE: 0.4897467853668941
predictions: [ 1.35086     0.03489107  1.0565269  -0.17359206 -0.01603088  0.034246952.29936886  1.65422797  0.01069662  0.02166392]
actual value: [1 0 1 0 0 0 1 1 0 0]
Cold-start MSE: 0.5663486183636738
cold start predictions: [-0.0112379   1.21811676  1.29267406  0.02357371 -0.39662406  1.06616664-0.10646269  0.00861482  1.22619736  0.09728943]
actual value: [0 1 1 0 1 1 0 0 1 0]
Regression completed..!!

最后,我們銷毀模型以釋放內(nèi)存:

reg_model.destroy()

因此,從訓(xùn)練數(shù)據(jù)集中刪除類別列會(huì)使 MSE 更小,但這樣做意味著我們無(wú)法解決冷啟動(dòng)建議問(wèn)題??紤]到我們使用相對(duì)較小的數(shù)據(jù)集的條件,實(shí)驗(yàn)結(jié)果很好。

正如預(yù)期的那樣,如果我們可以通過(guò)項(xiàng)目購(gòu)買和點(diǎn)擊訪問(wèn)完整的信息設(shè)置,則更容易生成預(yù)測(cè),但我們?nèi)匀恢皇褂脜R總的類別數(shù)據(jù)獲得冷啟動(dòng)建議的預(yù)測(cè)。

改進(jìn)的分解機(jī)

Web 應(yīng)用的許多預(yù)測(cè)任務(wù)需要對(duì)分類變量(例如用戶 ID)和人口統(tǒng)計(jì)信息(例如性別和職業(yè))進(jìn)行建模。為了應(yīng)用標(biāo)準(zhǔn) ML 技術(shù),需要通過(guò)單熱編碼(或任何其他技術(shù))將這些分類預(yù)測(cè)變換器轉(zhuǎn)換為一組二元特征。這使得得到的特征向量高度稀疏。要從這種稀疏數(shù)據(jù)中有效地學(xué)習(xí),考慮特征之間的相互作用是很重要的。

在上一節(jié)中,我們看到 FM 可以有效地應(yīng)用于模型二階特征交互。但是,FM 模型以線性方式進(jìn)行交互,如果您想捕獲真實(shí)世界數(shù)據(jù)的非線性和固有復(fù)雜結(jié)構(gòu),則這種方式是不夠的。

Xiangnan He 和 Jun Xiao 等。為了克服這一局限,我們提出了一些研究計(jì)劃,如神經(jīng)因子分解機(jī)(NFM)和注意因子分解機(jī)(AFM)。

有關(guān)更多信息,請(qǐng)參閱以下文章:

  • Xiangnan He 和 Tat-Seng Chua,用于稀疏預(yù)測(cè)分析的神經(jīng)分解機(jī)。在 SIGIR '17,新宿,東京,日本,2017 年 8 月 7 日至 11 日的會(huì)議錄。
  • Jun Xiao,Hao Ye,Xiantian He,Hanwang Zhang,Fei Wu 和 Tat-Seng Chua(2017),注意分解機(jī):通過(guò)注意網(wǎng)絡(luò)學(xué)習(xí)特征交互的權(quán)重 IJCAI,墨爾本,澳大利亞,2017 年 8 月 19 - 25 日。

通過(guò)在建模二階特征相互作用中無(wú)縫地組合 FM 的線性度和在建模高階特征相互作用中神經(jīng)網(wǎng)絡(luò)的非線性,NFM 可用于在稀疏設(shè)置下進(jìn)行預(yù)測(cè)。

另一方面,即使所有特征交互具有相同的權(quán)重,AFM 也可用于對(duì)數(shù)據(jù)建模,因?yàn)椴⒎撬刑卣鹘换ザ纪瑯佑杏们揖哂蓄A(yù)測(cè)性。

在下一節(jié)中,我們將看到使用 NFM 進(jìn)行電影推薦的示例。

神經(jīng)分解機(jī)

使用原始 FM 算法,其表現(xiàn)可能受到建模方式阻礙,它使用相同權(quán)重建模所有特征交互,因?yàn)椴⒎撬刑卣鹘换ザ纪瑯佑杏们揖哂蓄A(yù)測(cè)性。例如,與無(wú)用特征的交互甚至可能引入噪聲并對(duì)表現(xiàn)產(chǎn)生不利影響。

最近,Xiangnan H.等。提出了一種稱為神經(jīng)分解機(jī)(NFM)的 FM 算法的改進(jìn)版本。 NFM 在建模二階特征相互作用中無(wú)縫地結(jié)合了 FM 的線性度,在建模高階特征相互作用時(shí)無(wú)縫地結(jié)合了神經(jīng)網(wǎng)絡(luò)的非線性。從概念上講,NFM 比 FM 更具表現(xiàn)力,因?yàn)?FM 可以被看作是沒(méi)有隱藏層的 NFM 的特例。

數(shù)據(jù)集描述

我們使用 MovieLens 數(shù)據(jù)進(jìn)行個(gè)性化標(biāo)簽推薦。它包含電影上 668,953 個(gè)用戶的標(biāo)簽應(yīng)用。使用單熱編碼將每個(gè)標(biāo)簽應(yīng)用(用戶 ID,電影 ID 和標(biāo)簽)轉(zhuǎn)換為特征向量。這留下了 90,445 個(gè)二元特征,稱為ml-tag數(shù)據(jù)集。

我使用 Perl 腳本將其從.dat轉(zhuǎn)換為.libfm格式。轉(zhuǎn)換程序在此鏈接(第 2.2.1 節(jié))中描述。轉(zhuǎn)換后的數(shù)據(jù)集包含用于訓(xùn)練,驗(yàn)證和測(cè)試的文件,如下所示:

  • ml-tag.train.libfm
  • ml-tag.validation.libfm
  • ml-tag.test.libfm

有關(guān)此文件格式的更多信息,請(qǐng)參閱此鏈接。

NFM 和電影推薦

我們使用 TensorFlow 并復(fù)用來(lái)自這個(gè) GitHub 的擴(kuò)展 NFM 實(shí)現(xiàn)。這是 FM 的深度版本,與常規(guī) FM 相比更具表現(xiàn)力。倉(cāng)庫(kù)有三個(gè)文件,即NeuralFM.pyFM.pyLoadData.py

  • FM.py用于訓(xùn)練數(shù)據(jù)集。這是 FM 的原始實(shí)現(xiàn)。
  • NeuralFM.py用于訓(xùn)練數(shù)據(jù)集。這是 NFM 的原始實(shí)現(xiàn),但有一些改進(jìn)和擴(kuò)展。
  • LoadData.py用于以 libfm 格式預(yù)處理和加載數(shù)據(jù)集。

模型訓(xùn)練

首先,我們使用以下命令訓(xùn)練 FM 模型。該命令還包括執(zhí)行訓(xùn)練所需的參數(shù):

$ python3 FM.py --dataset ml-tag --epoch 20 --pretrain -1 --batch_size 4096 --lr 0.01 --keep 0.7
>>>
FM: dataset=ml-tag, factors=16, #epoch=20, batch=4096, lr=0.0100, lambda=0.0e+00, keep=0.70, optimizer=AdagradOptimizer, batch_norm=1 
#params: 1537566
Init: 	 train=1.0000, validation=1.0000 [5.7 s]
Epoch 1 [13.9 s]    train=0.5413, validation=0.6005 [7.8 s]
Epoch 2 [14.2 s]    train=0.4927, validation=0.5779 [8.3 s]
…
Epoch 19 [15.4 s]    train=0.3272, validation=0.5429 [8.1 s]
Epoch 20 [16.6 s]    train=0.3242, validation=0.5425 [7.8 s]

訓(xùn)練結(jié)束后,訓(xùn)練好的模型將保存在主目錄的pretrain文件夾中:

將模型保存為預(yù)訓(xùn)練文件。

此外,我已嘗試使用以下代碼進(jìn)行驗(yàn)證和訓(xùn)練損失的訓(xùn)練和驗(yàn)證誤差:

    # Plot loss over timeplt.plot(epoch_list, train_err_list, 'r--', label='FM training loss per epoch', linewidth=4)plt.title('FM training loss per epoch')plt.xlabel('Epoch')plt.ylabel('Training loss')plt.legend(loc='upper right')plt.show()# Plot accuracy over timeplt.plot(epoch_list, valid_err_list, 'r--', label='FM validation loss per epoch', linewidth=4)plt.title('FM validation loss per epoch')plt.xlabel('Epoch')plt.ylabel('Validation loss')plt.legend(loc='upper left')plt.show()

前面的代碼生成繪圖,顯示 FM 模型中每次迭代的訓(xùn)練與驗(yàn)證損失:

Model training

圖 11:FM 模型中每次迭代的訓(xùn)練與驗(yàn)證損失

如果查看前面的輸出日志,最佳訓(xùn)練(即驗(yàn)證和訓(xùn)練)將在第 20 次和最后一次迭代時(shí)進(jìn)行。但是,您可以進(jìn)行更多迭代以改進(jìn)訓(xùn)練,這意味著評(píng)估步驟中的 RMSE 值較低:

Best Iter(validation)= 20    train = 0.3242, valid = 0.5425 [490.9 s]

現(xiàn)在讓我們使用以下命令訓(xùn)練 NFM 模型(但也使用參數(shù)):

$ python3 NeuralFM.py --dataset ml-tag --hidden_factor 64 --layers [64] --keep_prob [0.8,0.5] --loss_type square_loss --activation relu --pretrain 0 --optimizer AdagradOptimizer --lr 0.01 --batch_norm 1 --verbose 1 --early_stop 1 --epoch 20
>>>
Neural FM: dataset=ml-tag, hidden_factor=64, dropout_keep=[0.8,0.5], layers=[64], loss_type=square_loss, pretrain=0, #epoch=20, batch=128, lr=0.0100, lambda=0.0000, optimizer=AdagradOptimizer, batch_norm=1, activation=relu, early_stop=1
#params: 5883150
Init:    train=0.9911, validation=0.9916, test=0.9920 [25.8 s]
Epoch 1 [60.0 s]    train=0.6297, validation=0.6739, test=0.6721 [28.7 s]
Epoch 2 [60.4 s]    train=0.5646, validation=0.6390, test=0.6373 [28.5 s]
…
Epoch 19 [53.4 s]    train=0.3504, validation=0.5607, test=0.5587 [25.7 s]
Epoch 20 [55.1 s]    train=0.3432, validation=0.5577, test=0.5556 [27.5 s]

此外,我嘗試使用以下代碼使驗(yàn)證和訓(xùn)練損失的訓(xùn)練和驗(yàn)證誤差可見(jiàn):

    # Plot test accuracy over timeplt.plot(epoch_list, test_err_list, 'r--', label='NFM test loss per epoch', linewidth=4)plt.title('NFM test loss per epoch')plt.xlabel('Epoch')plt.ylabel('Test loss')plt.legend(loc='upper left')plt.show()

前面的代碼在 NFM 模型中產(chǎn)生每次迭代的訓(xùn)練與驗(yàn)證損失:

Model training

圖 12:NFM 模型中每次迭代的訓(xùn)練與驗(yàn)證損失

對(duì)于 NFM 模型,最佳訓(xùn)練(用于驗(yàn)證和訓(xùn)練)發(fā)生在第 20 次和最后一次迭代。但是,您可以進(jìn)行更多迭代以改進(jìn)訓(xùn)練,這意味著評(píng)估步驟中的 RMSE 值較低:

Best Iter (validation) = 20   train = 0.3432, valid = 0.5577, test = 0.5556 [1702.5 s]

模型評(píng)估

現(xiàn)在,要評(píng)估原始 FM 模型,請(qǐng)執(zhí)行以下命令:

$ python3 FM.py --dataset ml-tag --epoch 20 --batch_size 4096 --lr 0.01 --keep 0.7 --process evaluate
Test RMSE: 0.5427

注意

對(duì)于 TensorFlow 上的 Attentional Factorization Machines 實(shí)現(xiàn),感興趣的讀者可以參考此鏈接中的 GitHub 倉(cāng)庫(kù)。但請(qǐng)注意,某些代碼可能無(wú)效。我將它們更新為兼容 TensorFlow v1.6。因此,我強(qiáng)烈建議您使用本書提供的代碼。

要評(píng)估 NFM 模型,只需將以下行添加到NeuralFM.py腳本中的main()方法,如下所示:

# Model evaluation
print("RMSE: ")
print(model.evaluate(data.Test_data)) #evaluate on test set
>>>
RMSE: 0.5578330373003925

因此,RMSE 幾乎與 FM 模型相同。現(xiàn)在讓我們看看每次迭代的測(cè)試誤差:

# Plot test accuracy over time
plt.plot(epoch_list, test_err_list, 'r--', label='NFM test loss per epoch', linewidth=4)
plt.title('NFM test loss per epoch')
plt.xlabel('Epoch')
plt.ylabel('Test loss')
plt.legend(loc='upper left')
plt.show()

前面的代碼繪制了 NFM 模型中每次迭代的測(cè)試損失:

Model evaluation

圖 13:NFM 模型中每次迭代的測(cè)試損失

總結(jié)

在本章中,我們討論了如何使用 TensorFlow 開(kāi)發(fā)可擴(kuò)展的推薦系統(tǒng)。我們已經(jīng)看到推薦系統(tǒng)的一些理論背景,并在開(kāi)發(fā)推薦系統(tǒng)時(shí)使用協(xié)同過(guò)濾方法。在本章后面,我們看到了如何使用 SVD 和 K 均值來(lái)開(kāi)發(fā)電影推薦系統(tǒng)。

最后,我們了解了如何使用 FM 和一種稱為 NFM 的變體來(lái)開(kāi)發(fā)更準(zhǔn)確的推薦系統(tǒng),以便處理大規(guī)模稀疏矩陣。我們已經(jīng)看到處理冷啟動(dòng)問(wèn)題的最佳方法是使用 FM 協(xié)同過(guò)濾方法。

下一章是關(guān)于設(shè)計(jì)由批評(píng)和獎(jiǎng)勵(lì)驅(qū)動(dòng)的 ML 系統(tǒng)。我們將看到如何應(yīng)用 RL 算法為現(xiàn)實(shí)數(shù)據(jù)集制作預(yù)測(cè)模型。

十、OpenAI Gym

OpenAI Gym 是一個(gè)開(kāi)源 Python 框架,由非營(yíng)利性 AI 研究公司 OpenAI 開(kāi)發(fā),作為開(kāi)發(fā)和評(píng)估 RL 算法的工具包。它給我們提供了一組測(cè)試問(wèn)題,稱為環(huán)境,我們可以編寫 RL 算法來(lái)解決。這使我們能夠?qū)⒏嗟臅r(shí)間用于實(shí)現(xiàn)和改進(jìn)學(xué)習(xí)算法,而不是花費(fèi)大量時(shí)間來(lái)模擬環(huán)境。此外,它為人們提供了一種比較和審查其他算法的媒介。

OpenAI 環(huán)境

OpenAI Gym 擁有一系列環(huán)境。在編寫本書時(shí),可以使用以下環(huán)境:

  • 經(jīng)典控制和玩具文本:來(lái)自 RL 文獻(xiàn)的小規(guī)模任務(wù)。
  • 算法:執(zhí)行計(jì)算,例如添加多位數(shù)和反轉(zhuǎn)序列。這些任務(wù)中的大多數(shù)都需要記憶,并且可以通過(guò)改變序列長(zhǎng)度來(lái)改變它們的難度。
  • Atari:經(jīng)典 Atari 游戲,使用街機(jī)學(xué)習(xí)環(huán)境,屏幕圖像或 RAM 作為輸入。
  • 棋盤游戲:目前,我們已經(jīng)將圍棋游戲包括在9x919x19板上,而 Pachi 引擎則作為對(duì)手。
  • 2D 和 3D 機(jī)器人:允許在模擬中控制機(jī)器人。這些任務(wù)使用 MuJoCo 物理引擎,該引擎專為快速準(zhǔn)確的機(jī)器人仿真而設(shè)計(jì)。一些任務(wù)改編自 RLLab。

env

OpenAI Gym 允許使用env類,它封裝了環(huán)境和任何內(nèi)部動(dòng)態(tài)。此類具有不同的方法和屬性,使您可以實(shí)現(xiàn)創(chuàng)建新環(huán)境。最重要的方法名為resetsteprender

  • reset方法的任務(wù)是通過(guò)將環(huán)境初始化為初始狀態(tài)來(lái)重置環(huán)境。在重置方法中,必須包含構(gòu)成環(huán)境的元素的定義(在這種情況下,機(jī)械臂的定義,要抓取的對(duì)象及其支持)。
  • step方法是用于在時(shí)間上推進(jìn)環(huán)境的 。它需要輸入操作并將新觀察結(jié)果返回給智能體。在該方法中,必須定義運(yùn)動(dòng)動(dòng)態(tài)管理,狀態(tài)和獎(jiǎng)勵(lì)計(jì)算以及劇集完成控制。
  • 最后一種方法是render, 用于顯示當(dāng)前狀態(tài)。

使用框架提出的env類作為新環(huán)境的基礎(chǔ),它采用工具包提供的通用接口。

這樣,構(gòu)建的環(huán)境可以集成到工具包的庫(kù)中,并且可以從 OpenAI Gym 社區(qū)的用戶所做的算法中學(xué)習(xí)它們的動(dòng)態(tài)。

安裝并運(yùn)行 OpenAI Gym

有關(guān)如何使用和運(yùn)行 OpenAI Gym 的更多詳細(xì)說(shuō)明,請(qǐng)參閱(此鏈接)的官方文檔頁(yè)面。使用以下命令可以實(shí)現(xiàn) OpenApp Gym 的最小安裝:

git clone https://github.com/openai/gym
cd gym
pip install -e

安裝 OpenAI Gym 之后,您可以在 Python 代碼中實(shí)例化并運(yùn)行環(huán)境:

import gym
env = gym.make('CartPole-v0')obs = env.reset()for step_idx in range(500):env.render()obs, reward, done, _ = env.step(env.action_space.sample())

此代碼段將首先導(dǎo)入gym庫(kù)。然后它創(chuàng)建了 Cart-Pole 環(huán)境的實(shí)例 ,這是 RL 中的經(jīng)典問(wèn)題。 Cart-Pole 環(huán)境模擬安裝在推車上的倒立擺。鐘擺最初是垂直的,你的目標(biāo)是保持其垂直平衡。控制擺錘的唯一方法是選擇水平方向讓推車移動(dòng)(向左或向右)。

上面的代碼運(yùn)行500時(shí)間步的環(huán)境,并選擇隨機(jī)操作在每一步執(zhí)行。因此,您在下面的視頻中看到,桿不能長(zhǎng)時(shí)間保持穩(wěn)定。獎(jiǎng)勵(lì)是通過(guò)在桿距垂直方向超過(guò) 15 度之前經(jīng)過(guò)的時(shí)間步數(shù)來(lái)衡量的。您保持在此范圍內(nèi)的時(shí)間越長(zhǎng),您的總獎(jiǎng)勵(lì)就越高。

Q-Learning 算法

解決 RL 問(wèn)題需要在學(xué)習(xí)過(guò)程中估計(jì)評(píng)估函數(shù)。該函數(shù)必須能夠通過(guò)獎(jiǎng)勵(lì)的總和來(lái)評(píng)估策略的成功。

Q-Learning 的基本思想是算法學(xué)習(xí)整個(gè)狀態(tài)和動(dòng)作空間的最優(yōu)評(píng)估函數(shù)(S×A)。這種所謂的Q函數(shù)以Q: S×A -> R的形式提供匹配,其中R是在狀態(tài)s ∈ S中執(zhí)行的動(dòng)作a ∈ A的未來(lái)獎(jiǎng)勵(lì)的期望值。一旦智能體學(xué)會(huì)了最佳函數(shù)Q,它就能夠識(shí)別哪種行為將導(dǎo)致某種狀態(tài)下最高的未來(lái)獎(jiǎng)勵(lì)。

實(shí)現(xiàn) Q-Learning 算法的最常用示例之一涉及使用表。該表的每個(gè)單元格是值Q(s; a) = R并且它被初始化為 0.由智能體執(zhí)行的動(dòng)作a ∈ A是使用相對(duì)于的貪婪策略來(lái)選擇的。

Q-Learning 算法的基本思想是訓(xùn)練規(guī)則,它更新表格元素Q(s; a) = R。

該算法遵循以下基本步驟:

  1. 任意初始化Q(s; a) = R
  2. 對(duì)每個(gè)劇集重復(fù)以下:
    1. 初始化s。

    2. 重復(fù)(對(duì)于劇集的每一步):

    3. 使用從Q派生的策略從s ∈ S中選擇一個(gè)動(dòng)作a ∈ A。

    4. 選取動(dòng)作a,觀察rs'

      Q(s; a) <= Q(s; a) + a · (r + γ · max Q(s'; a) - Q(s; a))
      s': s <- s'
      
    5. 繼續(xù)直到s的終點(diǎn)。

我們?cè)谙聢D中描述了算法:

The Q-Learning algorithm

圖 2:Q-Learning 算法

讓我們總結(jié)一下 Q 值更新過(guò)程中使用的參數(shù):

  • α是學(xué)習(xí)率,設(shè)置在 0 和 1 之間。將其設(shè)置為 0 意味著 Q 值永遠(yuǎn)不會(huì)更新,因此不會(huì)學(xué)習(xí)任何內(nèi)容。設(shè)置較高的值(如 0.9)意味著可以快速進(jìn)行學(xué)習(xí)。
  • γ是折扣因子,設(shè)置在 0 和 1 之間。這模擬了未來(lái)獎(jiǎng)勵(lì)的值低于直接獎(jiǎng)勵(lì)的事實(shí)。在數(shù)學(xué)上,需要將折扣因子設(shè)置為小于 1 以使算法收斂。
  • max Q(s'; a)是在當(dāng)前狀態(tài)之后的狀態(tài)下可獲得的最大獎(jiǎng)勵(lì),即之后采取最佳行動(dòng)的獎(jiǎng)勵(lì)。

FrozenLake 環(huán)境

智能體控制角色在4×4網(wǎng)格世界中的移動(dòng)。網(wǎng)格的一些瓷磚是可行走的,而其他瓷磚則導(dǎo)致落入水中。另外,智能體的移動(dòng)方向是不確定的,并且僅部分地取決于所選擇的方向。智能體因找到目標(biāo)圖塊的可行走路徑而獲得獎(jiǎng)勵(lì):

The FrozenLake environment

圖 3:Frozen-Lake v0 網(wǎng)格字的表示

使用如下網(wǎng)格描述上面所示的表面:

SFFF   (S: starting point, safe)
FHFH   (F: frozensurface, safe)
FFFH   (H: hole, fall to yourdoom)
HFFG   (G: goal, where the frisbee islocated)

當(dāng)我們到達(dá)目標(biāo)或陷入一個(gè)洞時(shí),這一集結(jié)束。如果達(dá)到目標(biāo),我們會(huì)收到1的獎(jiǎng)勵(lì),否則會(huì)收到0。

針對(duì) FrozenLake 問(wèn)題的 Q-Learning

在為高度結(jié)構(gòu)化數(shù)據(jù)提供良好功能方面,神經(jīng)網(wǎng)絡(luò)非常強(qiáng)大。

為了解決 FrozenLake 問(wèn)題,我們將構(gòu)建一個(gè)單層網(wǎng)絡(luò),該網(wǎng)絡(luò)采用1×16向量中編碼的狀態(tài)并學(xué)習(xí)最佳移動(dòng)(動(dòng)作),在向量中映射可能的動(dòng)作長(zhǎng)度為四。

以下實(shí)現(xiàn)基于 TensorFlow:

首先,我們需要導(dǎo)入所有庫(kù):

import gym
import numpy as np
import random
import tensorflow as tf
import matplotlib.pyplot as plt

然后我們加載并設(shè)置環(huán)境以進(jìn)行測(cè)試:

env = gym.make('FrozenLake-v0')

輸入網(wǎng)絡(luò)是一種狀態(tài),以張量形狀[1,16]編碼。因此,我們定義了input1占位符:

inputs1 = tf.placeholder(shape=[1,16],dtype=tf.float32)

網(wǎng)絡(luò)權(quán)重最初由tf.random_uniform函數(shù)隨機(jī)選擇:

W = tf.Variable(tf.random_uniform([16,4],0,0.01))

網(wǎng)絡(luò)輸出由inputs1占位符和權(quán)重的乘積給出:

Qout = tf.matmul(inputs1,W)

Qout上計(jì)算的argmax將給出預(yù)測(cè)值:

predict = tf.argmax(Qout,1)

最佳動(dòng)作(nextQ)以張量形狀編碼[1,4]:

nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)

接下來(lái),我們定義一個(gè)損失函數(shù)來(lái)實(shí)現(xiàn)反向傳播過(guò)程。

損失函數(shù)是loss = ∑(Q - target - Q),其中計(jì)算當(dāng)前預(yù)測(cè)的 Q 值和目標(biāo)值之間的差異,并且梯度通過(guò)網(wǎng)絡(luò)傳遞:

loss = tf.reduce_sum(tf.square(nextQ - Qout))

優(yōu)化函數(shù)是眾所周知的GradientDescentOptimizer

trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)

重置并初始化計(jì)算圖:

tf.reset_default_graph()
init = tf.global_variables_initializer()

然后我們?cè)O(shè)置 Q-Learning 訓(xùn)練過(guò)程的參數(shù):

y = .99
e = 0.1
num_episodes = 6000jList = []
rList = []

我們定義會(huì)話sess,其中網(wǎng)絡(luò)必須學(xué)習(xí)最佳的移動(dòng)順序:

with tf.Session() as sess:sess.run(init)for i in range(num_episodes):s = env.reset()rAll = 0d = Falsej = 0while j < 99:j+=1

輸入狀態(tài)用于為網(wǎng)絡(luò)提供信息:

            a,allQ = sess.run([predict,Qout],\feed_dict=\{inputs1:np.identity(16)[s:s+1]})

從輸出張量a中選擇一個(gè)隨機(jī)狀態(tài):

            if np.random.rand(1) < e:a[0] = env.action_space.sample()

使用env.step()函數(shù)執(zhí)行a[0]動(dòng)作,獲得獎(jiǎng)勵(lì),r和狀態(tài),s1

                     s1,r,d,_ = env.step(a[0])

新?tīng)顟B(tài)s1用于更新Q張量:

            Q1 = sess.run(Qout,feed_dict=\{inputs1:np.identity(16)[s1:s1+1]})maxQ1 = np.max(Q1)targetQ = allQtargetQ[0,a[0]] = r + y*maxQ1

當(dāng)然,必須為反向傳播過(guò)程更新權(quán)重:

           _,W1 = sess.run([updateModel,W],\feed_dict=\{inputs1:np.identity(16)[s:s+1],nextQ:targetQ})

rAll這里定義了會(huì)話期間獲得的總獎(jiǎng)勵(lì)。讓我們回想一下,RL 智能體的目標(biāo)是最大化它從長(zhǎng)遠(yuǎn)來(lái)看所獲得的總獎(jiǎng)勵(lì):

rAll += r

更新下一步的環(huán)境狀態(tài):

          s = s1if d == True:e = 1./((i/50) + 10)breakjList.append(j)rList.append(rAll)

計(jì)算結(jié)束時(shí),將顯示成功劇集的百分比:

print ("Percent of successfulepisodes: " +\
str(sum(rList)/num_episodes) + "%")

如果我們運(yùn)行模型,我們應(yīng)該得到這樣的結(jié)果,可以通過(guò)調(diào)整網(wǎng)絡(luò)參數(shù)來(lái)改進(jìn):

>>>[2017-01-15 16:56:01,048] Making new env: FrozenLake-v0
Percentage of successful episodes: 0.558%

深度 Q 學(xué)習(xí)

多虧了 DeepMind 在 2013 年和 2016 年取得的近期成就,它成功地在 Atari 游戲中達(dá)到了所謂的超人級(jí)別,并擊敗了圍棋世界冠軍,RL 在機(jī)器學(xué)習(xí)社區(qū)中變得非常有趣。這種新的興趣也是由于深度神經(jīng)網(wǎng)絡(luò)(DNN)表現(xiàn)為近似函數(shù),使這種算法的潛在價(jià)值達(dá)到了更高的水平。最近獲得最多興趣的算法肯定是深度 Q-Learning。以下部分介紹深度 Q-Learning 算法,并討論一些優(yōu)化技術(shù)以最大化其表現(xiàn)。

深度 Q 神經(jīng)網(wǎng)絡(luò)

當(dāng)狀態(tài)數(shù)和可能的動(dòng)作增加并且從矩陣的角度來(lái)看變得難以管理時(shí),Q 學(xué)習(xí)基礎(chǔ)算法會(huì)引起巨大的問(wèn)題。想想谷歌使用的結(jié)構(gòu)輸入配置,以達(dá)到 Atari 游戲的表現(xiàn)水平。狀態(tài)空間是離散的,但國(guó)家的數(shù)量是巨大的。這就是深度學(xué)習(xí)的步驟。神經(jīng)網(wǎng)絡(luò)非常擅長(zhǎng)為高度結(jié)構(gòu)化的數(shù)據(jù)提供良好的功能。事實(shí)上,我們可以用神經(jīng)網(wǎng)絡(luò)識(shí)別 Q 函數(shù),它將狀態(tài)和動(dòng)作作為輸入并輸出相應(yīng)的 Q 值:

Q(state; action) = value

深度神經(jīng)網(wǎng)絡(luò)最常見(jiàn)的實(shí)現(xiàn)如下圖所示:

Deep Q neural networks

圖 4:深度 Q 神經(jīng)網(wǎng)絡(luò)的通用實(shí)現(xiàn)

或者, 可以將狀態(tài)作為輸入,并為每個(gè)可能的動(dòng)作生成相應(yīng)的值:

Q(state) = 每個(gè)可能的操作值

這個(gè)優(yōu)化的實(shí)現(xiàn)可以在下圖中看到:

Deep Q neural networks

圖 5:深度 Q 神經(jīng)網(wǎng)絡(luò)的優(yōu)化實(shí)現(xiàn)

最后的方法在計(jì)算上是有利的,因?yàn)橐?Q 值(或選擇最高的 Q 值),我們只需要通過(guò)網(wǎng)絡(luò)向前邁出一步,我們將立即獲得所有可用操作的所有 Q 值。

Cart-Pole 問(wèn)題

我們將建立一個(gè)深度神經(jīng)網(wǎng)絡(luò),可以學(xué)習(xí)通過(guò) RL 玩游戲。更具體地說(shuō),我們將使用深度 Q 學(xué)習(xí)訓(xùn)練智能體玩 Cart-Pole 游戲。

在這個(gè)游戲中, 自由擺動(dòng)桿連接到推車。推車可以向左和向右移動(dòng),目標(biāo)是盡可能長(zhǎng)時(shí)間保持桿直立:

The Cart-Pole problem

圖 6:Cart-Pole

我們使用 OpenAI Gym 模擬這個(gè)游戲。我們需要導(dǎo)入所需的庫(kù):

import gym
import tensorflow as tf
import numpy as np
import time

讓我們創(chuàng)建 Cart-Pole 游戲環(huán)境:

env = gym.make('CartPole-v0')

初始化環(huán)境,獎(jiǎng)勵(lì)列表和開(kāi)始時(shí)間:

env.reset()
rewards = []
tic = time.time()

這里使用env.render()語(yǔ)句來(lái)顯示運(yùn)行模擬的窗口:

for _ in range(1000):env.render()

env.action_space.sample()被傳遞給env.step()語(yǔ)句以構(gòu)建模擬的下一步:

    state, reward, done, info = \env.step\(env.action_space.sample())

在 Cart-Pole 游戲中,有兩種可能的動(dòng)作:向左或向右移動(dòng)。因此,我們可以采取兩種操作,編碼為 0 和 1。

在這里,我們采取隨機(jī)行動(dòng):

    rewards.append(reward)if done:rewards = []env.reset()
toc = time.time()

10 秒后,模擬結(jié)束:

if toc-tic > 10:env.close()

要關(guān)閉顯示模擬的窗口,請(qǐng)使用env.close()。

當(dāng)我們運(yùn)行模擬時(shí),我們有一個(gè)獎(jiǎng)勵(lì)列表,如下所示:

[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]

在極點(diǎn)超過(guò)一定角度后,游戲重置。對(duì)于正在運(yùn)行的每個(gè)幀,模擬返回 1.0 的獎(jiǎng)勵(lì)。游戲運(yùn)行的時(shí)間越長(zhǎng),獲得的獎(jiǎng)勵(lì)就越多。因此,我們的網(wǎng)絡(luò)的目標(biāo)是通過(guò)保持桿垂直來(lái)最大化獎(jiǎng)勵(lì)。它將通過(guò)向左和向右移動(dòng)推車來(lái)完成此操作。

針對(duì) Cart-Pole 問(wèn)題的深度 Q 網(wǎng)絡(luò)

我們使用 Bellman 方程再次訓(xùn)練我們的 Q 學(xué)習(xí)智能體:

Deep Q-Network for the Cart-Pole problem

這里,s是狀態(tài),a是動(dòng)作,s'是狀態(tài)s和動(dòng)作a的下一個(gè)狀態(tài)。

之前,我們使用這個(gè)等式來(lái)學(xué)習(xí) Q 表的值。但是,這個(gè)游戲有很多狀態(tài)可供使用。狀態(tài)有四個(gè)值:推車的位置和速度,以及桿的位置和速度。這些都是實(shí)數(shù)值,所以如果我們忽略浮點(diǎn)精度,我們實(shí)際上有無(wú)限狀態(tài)。然后,我們將用一個(gè)近似于 Q 表查找功能的神經(jīng)網(wǎng)絡(luò)替換它,而不是使用表格。

通過(guò)將狀態(tài)傳遞到網(wǎng)絡(luò)來(lái)計(jì)算 Q 值,而輸出將是每個(gè)可用動(dòng)作的 Q 值,具有完全連接的隱藏層:

Deep Q-Network for the Cart-Pole problem

圖 7:深度 Q-Learning

在這個(gè) Cart-Pole 游戲中,我們有四個(gè)輸入,一個(gè)用于狀態(tài)中的每個(gè)值;和兩個(gè)輸出,每個(gè)動(dòng)作一個(gè)。網(wǎng)絡(luò)權(quán)重更新將通過(guò)選擇動(dòng)作并使用所選動(dòng)作模擬游戲來(lái)進(jìn)行。這將把我們帶到下一個(gè)狀態(tài)然后再到獎(jiǎng)勵(lì)。

以下是用于解決 Cart-Pole 問(wèn)題的神經(jīng)網(wǎng)絡(luò)的簡(jiǎn)短代碼片段:

import tensorflow as tf
class DQNetwork:def __init__(self,\learning_rate=0.01, \state_size=4,\action_size=2, \hidden_size=10,\name='DQNetwork'):

隱藏層由兩個(gè)完全連接的層組成,具有 ReLU 激活:

            self.fc1 =tf.contrib.layers.fully_connected\(self.inputs_,\hidden_size)self.fc2 = tf.contrib.layers.fully_connected\(self.fc1,\hidden_size)

輸出層是線性輸出層:

            self.output = tf.contrib.layers.fully_connected\(self.fc2,\action_size,activation_fn=None)

經(jīng)驗(yàn)重放方法

近似函數(shù)可能會(huì)受到非獨(dú)立且相同分布和非平穩(wěn)數(shù)據(jù)(狀態(tài)之間的相關(guān)性)的影響。

使用經(jīng)驗(yàn)重放方法可以克服這種問(wèn)題。

在智能體和環(huán)境之間的交互過(guò)程中,所有經(jīng)驗(yàn)(stateactionrewardnext_state)都保存在重放內(nèi)存中,這是固定大小的內(nèi)存并以先進(jìn)先出(FIFO)的方式運(yùn)行。

這是重放內(nèi)存類的實(shí)現(xiàn):

from collections import deque
import numpy as npclass replayMemory():def __init__(self, max_size = 1000):self.buffer = \deque(maxlen=max_size)def build(self, experience):self.buffer.append(experience)def sample(self, batch_size):idx = np.random.choice\(np.arange(len(self.buffer)), size=batch_size, replace=False)return [self.buffer[ii] for ii in idx]

這將允許在網(wǎng)絡(luò)訓(xùn)練期間使用在重放存儲(chǔ)器中隨機(jī)獲取的小批量經(jīng)驗(yàn),而不是一個(gè)接一個(gè)地使用最近的經(jīng)驗(yàn)。

使用經(jīng)驗(yàn)重放方法有助于緩解順序訓(xùn)練數(shù)據(jù)的問(wèn)題,這可能導(dǎo)致算法保持在局部最小值,從而使其無(wú)法獲得最佳解決方案。

利用和探索

每當(dāng)智能體必須選擇要采取的行動(dòng)時(shí),它基本上有兩種方式可以執(zhí)行其策略。第一種模式稱為利用,包括根據(jù)目前獲得的信息,即過(guò)去和存儲(chǔ)的經(jīng)驗(yàn),做出最佳決策。此信息始終作為值函數(shù)提供,該函數(shù)表示哪個(gè)操作為每個(gè)狀態(tài) - 操作對(duì)提供最大的最終累積回報(bào)。

第二種模式稱為探索,它是一種決策策略,不同于目前認(rèn)為最優(yōu)的策略。

探索階段非常重要,因?yàn)樗糜谑占刺綔y(cè)狀態(tài)的信息。實(shí)際上,只有執(zhí)行最佳操作的智能體才有可能始終遵循相同的操作順序,而沒(méi)有機(jī)會(huì)探索并發(fā)現(xiàn)可能存在從長(zhǎng)遠(yuǎn)來(lái)看可能導(dǎo)致的策略更好的結(jié)果,即使這意味著立即獲得的收益更低。

最常用于在利用和探索之間達(dá)成正確妥協(xié)的策略是貪婪的策略。它代表了一種選擇動(dòng)作的方法,基于使用均勻概率分布來(lái)選擇隨機(jī)動(dòng)作的可能性。

深度 Q-Learning 訓(xùn)練算法

讓我們看看如何構(gòu)建一個(gè)深度 Q-Learning 算法來(lái)解決 Cart-Pole 問(wèn)題。

該項(xiàng)目相當(dāng)復(fù)雜。為此原因。它已被細(xì)分為幾個(gè)文件模塊:

  • DQNetwork.py:實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)
  • memory.py:實(shí)現(xiàn)經(jīng)驗(yàn)重放方法
  • start_simulation.py:創(chuàng)建我們想要解決的 Cart-Pole 環(huán)境
  • solve_cart_pole.py:用經(jīng)過(guò)訓(xùn)練的神經(jīng)網(wǎng)絡(luò)解決 Cart-Pole 環(huán)境
  • plot_result_DQN.py:繪制最后的獎(jiǎng)勵(lì)與劇集
  • deepQlearning.py:主程序

以下命令提供了deepQlearning.py文件實(shí)現(xiàn)的簡(jiǎn)要說(shuō)明:

import tensorflow as tf
import gym
import numpy as np
import time
import os
from create_cart_pole_env import *
from DQNetwork import *
from memory import *
from solve_cart_pole import *
from plot_result_DQN import *

接下來(lái)要做的是定義用于此實(shí)現(xiàn)的超參數(shù),因此我們需要定義要學(xué)習(xí)的最大劇集數(shù),一集中的最大步數(shù)以及未來(lái)的獎(jiǎng)勵(lì)折扣:

train_episodes = 1000
max_steps = 200
gamma = 0.99

探索參數(shù)是探索開(kāi)始時(shí)的探索概率,最小探索概率和探索概率的指數(shù)衰減率:

explore_start = 1.0
explore_stop = 0.01
decay_rate = 0.0001 

網(wǎng)絡(luò)參數(shù)是每個(gè)隱藏 Q 網(wǎng)絡(luò)層中的單元數(shù)和 Q 網(wǎng)絡(luò)學(xué)習(xí)率:

hidden_size = 64
learning_rate = 0.0001

定義以下內(nèi)存參數(shù):

memory_size = 10000             
batch_size = 20 

然后我們有大量的經(jīng)驗(yàn)來(lái)預(yù)先記錄內(nèi)存:

pretrain_length = batch_size

現(xiàn)在我們可以創(chuàng)建環(huán)境并啟動(dòng) Cart-Pole 模擬:

env = gym.make('CartPole-v0')
start_simulation(env)

接下來(lái),我們使用hidden_sizelearning_rate超參數(shù)實(shí)例化 DNN:

tf.reset_default_graph()
deepQN = DQNetwork(name='main', hidden_size=64, \learning_rate=0.0001)

最后,我們重新初始化模擬:

env.reset()

讓我們采取隨機(jī)步驟,從中我們可以獲得狀態(tài)和獎(jiǎng)勵(lì):

state, rew, done, _ = env.step(env.action_space.sample())

實(shí)例化replayMemory對(duì)象以實(shí)現(xiàn)經(jīng)驗(yàn)重放方法:

memory = replayMemory(max_size=10000)

使用memory.build方法獲取隨機(jī)動(dòng)作的塊,來(lái)存儲(chǔ)相對(duì)經(jīng)驗(yàn),狀態(tài)和動(dòng)作:

pretrain_length= 20for j in range(pretrain_length):action = env.action_space.sample()next_state, rew, done, _ = \env.step(env.action_space.sample())if done:env.reset()memory.build((state,\action,\rew,\np.zeros(state.shape)))state, rew, done, _ = \env.step(env.action_space.sample())else:memory.build((state, action, rew, next_state))state = next_state

通過(guò)獲得的新經(jīng)驗(yàn),我們可以進(jìn)行神??經(jīng)網(wǎng)絡(luò)的訓(xùn)練:

rew_list = []
train_episodes = 100
max_steps=200with tf.Session() as sess:sess.run(tf.global_variables_initializer())step = 0for ep in range(1, train_episodes):tot_rew = 0t = 0while t < max_steps:step += 1explore_p = stop_exp + (start_exp - stop_exp)*\np.exp(-decay_rate*step)if explore_p > np.random.rand():action = env.action_space.sample()else:

然后我們計(jì)算 Q 狀態(tài):

                Qs = sess.run(deepQN.output, \feed_dict={deepQN.inputs_: \state.reshape\((1, *state.shape))})

我們現(xiàn)在可以獲得動(dòng)作:

                action = np.argmax(Qs)next_state, rew, done, _ = env.step(action)tot_rew += rewif done:next_state = np.zeros(state.shape)t = max_stepsprint('Episode: {}'.format(ep),'Total rew: {}'.format(tot_rew),'Training loss: {:.4f}'.format(loss),'Explore P: {:.4f}'.format(explore_p))rew_list.append((ep, tot_rew))memory.build((state, action, rew, next_state))env.reset()state, rew, done, _ = env.step\(env.action_space.sample())else:memory.build((state, action, rew, next_state))state = next_statet += 1batch_size = pretrain_length               states = np.array([item[0] for item \in memory.sample(batch_size)])actions = np.array([item[1] for item \in memory.sample(batch_size)])rews = np.array([item[2] for item in \memory.sample(batch_size)])next_states = np.array([item[3] for item\in memory.sample(batch_size)])

最后,我們開(kāi)始訓(xùn)練智能體。訓(xùn)練很慢,因?yàn)樗秩镜膸染W(wǎng)絡(luò)可以訓(xùn)練的慢:

 target_Qs = sess.run(deepQN.output, \feed_dict=\{deepQN.inputs_: next_states})target_Qs[(next_states == \np.zeros(states[0].shape))\.all(axis=1)] = (0, 0)targets = rews + 0.99 * np.max(target_Qs, axis=1)loss, _ = sess.run([deepQN.loss, deepQN.opt],feed_dict={deepQN.inputs_: states,deepQN.targetQs_: targets,deepQN.actions_: actions})env = gym.make('CartPole-v0')

要測(cè)試模型,我們調(diào)用以下函數(shù):

 solve_cart_pole(env,deepQN,state,sess)plot_result(rew_list)

這里是用于測(cè)試購(gòu)物車桿問(wèn)題的神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn):

import numpy as npdef solve_cart_pole(env,dQN,state,sess):test_episodes = 10test_max_steps = 400env.reset()for ep in range(1, test_episodes):t = 0while t < test_max_steps:env.render() Qs = sess.run(dQN.output, \feed_dict={dQN.inputs_: state.reshape\((1, *state.shape))})action = np.argmax(Qs)next_state, reward, done, _ = env.step(action)if done:t = test_max_stepsenv.reset()state, reward, done, _ =    env.step(env.action_space.sample())else:state = next_statet += 1     

最后,如果我們運(yùn)行deepQlearning.py腳本,我們應(yīng)該得到這樣的結(jié)果:

[2017-12-03 10:20:43,915] Making new env: CartPole-v0
[]
Episode: 1 Total reward: 7.0 Training loss: 1.1949 Explore P: 0.9993
Episode: 2 Total reward: 21.0 Training loss: 1.1786 Explore P: 0.9972
Episode: 3 Total reward: 38.0 Training loss: 1.1868 Explore P: 0.9935
Episode: 4 Total reward: 8.0 Training loss: 1.3752 Explore P: 0.9927
Episode: 5 Total reward: 9.0 Training loss: 1.6286 Explore P: 0.9918
Episode: 6 Total reward: 32.0 Training loss: 1.4313 Explore P: 0.9887
Episode: 7 Total reward: 19.0 Training loss: 1.2806 Explore P: 0.9868
……
Episode: 581 Total reward: 47.0 Training loss: 0.9959 Explore P: 0.1844
Episode: 582 Total reward: 133.0 Training loss: 21.3187 Explore P: 0.1821
Episode: 583 Total reward: 54.0 Training loss: 42.5041 Explore P: 0.1812
Episode: 584 Total reward: 95.0 Training loss: 1.5211 Explore P: 0.1795
Episode: 585 Total reward: 52.0 Training loss: 1.3615 Explore P: 0.1787
Episode: 586 Total reward: 78.0 Training loss: 1.1606 Explore P: 0.1774
…….
Episode: 984 Total reward: 199.0 Training loss: 0.2630 Explore P: 0.0103
Episode: 985 Total reward: 199.0 Training loss: 0.3037 Explore P: 0.0103
Episode: 986 Total reward: 199.0 Training loss: 256.8498 Explore P: 0.0103
Episode: 987 Total reward: 199.0 Training loss: 0.2177 Explore P: 0.0103
Episode: 988 Total reward: 199.0 Training loss: 0.3051 Explore P: 0.0103
Episode: 989 Total reward: 199.0 Training loss: 218.1568 Explore P: 0.0103
Episode: 990 Total reward: 199.0 Training loss: 0.1679 Explore P: 0.0103
Episode: 991 Total reward: 199.0 Training loss: 0.2048 Explore P: 0.0103
Episode: 992 Total reward: 199.0 Training loss: 0.4215 Explore P: 0.0102
Episode: 993 Total reward: 199.0 Training loss: 0.2133 Explore P: 0.0102
Episode: 994 Total reward: 199.0 Training loss: 0.1836 Explore P: 0.0102
Episode: 995 Total reward: 199.0 Training loss: 0.1656 Explore P: 0.0102
Episode: 996 Total reward: 199.0 Training loss: 0.2620 Explore P: 0.0102
Episode: 997 Total reward: 199.0 Training loss: 0.2358 Explore P: 0.0102
Episode: 998 Total reward: 199.0 Training loss: 0.4601 Explore P: 0.0102
Episode: 999 Total reward: 199.0 Training loss: 0.2845 Explore P: 0.0102
[2017-12-03 10:23:43,770] Making new env: CartPole-v0
>>>

隨著訓(xùn)練損失減少,總獎(jiǎng)勵(lì)增加。

在測(cè)試期間,推車桿完美平衡:

The Deep Q-Learning training algorithm

圖 8:解決了 Cart-Pole 問(wèn)題

為了可視化訓(xùn)練,我們使用了plot_result()函數(shù)(它在plot_result_DQN.py函數(shù)中定義)。

plot_result()函數(shù)繪制每集的總獎(jiǎng)勵(lì):

def plot_result(rew_list):eps, rews = np.array(rew_list).Tsmoothed_rews = running_mean(rews, 10)smoothed_rews = running_mean(rews, 10)  plt.plot(eps[-len(smoothed_rews):], smoothed_rews)plt.plot(eps, rews, color='grey', alpha=0.3)plt.xlabel('Episode')plt.ylabel('Total Reward')plt.show()

以下繪圖顯示,每個(gè)事件的總獎(jiǎng)勵(lì)隨著智能體改進(jìn)其對(duì)值函數(shù)的估計(jì)而增加:

The Deep Q-Learning training algorithm

總結(jié)

許多研究人員認(rèn)為,RL 是我們創(chuàng)造人工智能的最好方法。這是一個(gè)令人興奮的領(lǐng)域,有許多未解決的挑戰(zhàn)和巨大的潛力。雖然一開(kāi)始看起來(lái)很有挑戰(zhàn)性,但開(kāi)始使用 RL 并不是那么困難。在本章中,我們描述了 RL 的一些基本原理。

我們討論的主要內(nèi)容是 Q-Learning 算法。它的顯著特點(diǎn)是能夠在即時(shí)獎(jiǎng)勵(lì)和延遲獎(jiǎng)勵(lì)之間做出選擇。最簡(jiǎn)單的 Q-learning 使用表來(lái)存儲(chǔ)數(shù)據(jù)。當(dāng)監(jiān)視/控制的系統(tǒng)的狀態(tài)/動(dòng)作空間的大小增加時(shí),這很快就失去了可行性。

我們可以使用神經(jīng)網(wǎng)絡(luò)作為函數(shù)逼近器來(lái)克服這個(gè)問(wèn)題,該函數(shù)逼近器將狀態(tài)和動(dòng)作作為輸入并輸出相應(yīng)的 Q 值。

按照這個(gè)想法,我們使用 TensorFlow 框架和 OpenAI Gym 工具包實(shí)現(xiàn)了一個(gè) Q 學(xué)習(xí)神經(jīng)網(wǎng)絡(luò),以贏得 FrozenLake 游戲。

在本章的最后一部分,我們介紹了深度強(qiáng)化學(xué)習(xí)。在傳統(tǒng)的 RL 中,問(wèn)題空間非常有限,并且環(huán)境中只有少數(shù)可能的狀態(tài)。這是傳統(tǒng)方法的主要局限之一。多年來(lái),已經(jīng)有一些相對(duì)成功的方法能夠通過(guò)近似狀態(tài)來(lái)處理更大的狀態(tài)空間。

深度學(xué)習(xí)算法的進(jìn)步已經(jīng)在 RL 中引入了新的成功應(yīng)用浪潮,因?yàn)樗峁┝擞行幚砀呔S輸入數(shù)據(jù)(例如圖像)的機(jī)會(huì)。在這種情況下,訓(xùn)練有素的 DNN 可以被視為一種端到端的 RL 方法,其中智能體可以直接從其輸入數(shù)據(jù)中學(xué)習(xí)狀態(tài)抽象和策略近似。按照這種方法,我們實(shí)現(xiàn)了 DNN 來(lái)解決 Cart-Pole 問(wèn)題。

我們的 TensorFlow 深度學(xué)習(xí)之旅將在此結(jié)束。深度學(xué)習(xí)是一個(gè)非常有成效的研究領(lǐng)域;有許多書籍,課程和在線資源可以幫助讀者深入理論和編程。此外,TensorFlow 還提供了豐富的工具來(lái)處理深度學(xué)習(xí)模型。我希望本書的讀者成為 TensorFlow 社區(qū)的一部分,該社區(qū)非常活躍,并希望熱心人士盡快加入。

http://www.risenshineclean.com/news/26863.html

相關(guān)文章:

  • 群暉nas做網(wǎng)站怎么可以讓百度快速收錄視頻
  • 在web服務(wù)器做網(wǎng)站高端網(wǎng)站公司
  • 網(wǎng)站建設(shè)推廣優(yōu)化有哪些基本方法seo課程多少錢
  • 濰坊網(wǎng)站建設(shè)哪家好高端定制網(wǎng)站建設(shè)
  • 全企網(wǎng)建站怎么樣網(wǎng)站流量數(shù)據(jù)
  • 石家莊哪個(gè)公司做網(wǎng)站好百度競(jìng)價(jià)代運(yùn)營(yíng)
  • 開(kāi)發(fā)一個(gè)網(wǎng)站需要多少時(shí)間代寫文章接單平臺(tái)
  • 珠海自適應(yīng)網(wǎng)站設(shè)計(jì)做網(wǎng)站哪家好
  • 個(gè)人買域名有什么用徐州seo培訓(xùn)
  • 做網(wǎng)站需要多大的內(nèi)存品牌推廣公司
  • 學(xué)生做網(wǎng)站賺錢網(wǎng)站推廣的常用方法有哪些?
  • 厚街鎮(zhèn)網(wǎng)站仿做seo關(guān)鍵詞排名優(yōu)化制作
  • 泉州網(wǎng)站建設(shè)推廣賺錢的軟件排行
  • 名詞解釋 網(wǎng)站內(nèi)容軟文推廣文章案例
  • 國(guó)內(nèi)vps做網(wǎng)站要備案嗎太原seo排名公司
  • 淘寶網(wǎng)站的論壇做的怎么樣寧波seo托管公司
  • 國(guó)家企業(yè)信息公示官網(wǎng)入口seo推廣系統(tǒng)排名榜
  • 上海城鄉(xiāng)住房建設(shè)部網(wǎng)站首頁(yè)近期的重大新聞
  • 網(wǎng)站建設(shè)有利于優(yōu)化seo教程技術(shù)
  • 鹽城公司做網(wǎng)站自己建站的網(wǎng)站
  • 泗水做網(wǎng)站普通話的順口溜6句
  • 做網(wǎng)站開(kāi)發(fā)需要的英語(yǔ)水平推廣賺錢軟件
  • 蘭州網(wǎng)站建站搜索引擎優(yōu)化論文3000字
  • 軟件網(wǎng)站開(kāi)發(fā)實(shí)訓(xùn)報(bào)告東莞網(wǎng)站排名提升
  • 做圍棋死活題的網(wǎng)站百度關(guān)鍵詞怎么優(yōu)化
  • cms網(wǎng)站開(kāi)發(fā)網(wǎng)站模板樂(lè)事薯片軟文推廣
  • 河北省住房和城鄉(xiāng)建設(shè)廳信用網(wǎng)站知乎營(yíng)銷推廣
  • wordpress pluto安康地seo
  • 義烏獨(dú)立站百度網(wǎng)盤在線登錄
  • 西安做網(wǎng)站的網(wǎng)絡(luò)公司優(yōu)化方案模板