91精品国产综合久久四虎久久_国产成人午夜高潮毛片_99er视频精品免费观看_2020亚洲熟女在线观看_日本女优人体写真_国内黄色毛片_年轻的老师中文版在线_丰满女邻居做爰_久久久久久精品成人免费图片

使用Apache MXNet進行情感分析
用深度神經(jīng)網(wǎng)絡來理解非結構化的文本數(shù)據(jù)
編者注:在這里可以下載本教程用到的所有Jupyter Notebook文件。
更多人工智能內容請關注2018年4月10-13日人工智能北京大會。

情感分析是數(shù)據(jù)科學界里一個常見的任務。一個企業(yè)可能想監(jiān)控它的產(chǎn)品在Twitter或是Facebook上被提及的次數(shù),從而能主動察覺(和解決)客戶滿意度的問題。但是人類的語言是豐富和復雜的,對一些東西有異常多的方式表達正面和負面情緒。反過來,對每種感情也有非常多的表述的方式。在眾多的情感分析的機器學習技術里,深度學習已經(jīng)被證明是能極好地理解這些復雜的內容了。

在本教程里,我們將會使用Apache MXNet構建一個神經(jīng)網(wǎng)絡來對情感進行分類。最終,我們會構建一個分類器,使用電影評論作為輸入,試圖去識別出對于它所評論的電影是有正面還是負面觀點。我們會從一個簡單的密集連接模型開始,然后會構建一個和Yoon Kim的這篇論文里類似的卷積網(wǎng)絡架構的模型。我們還會使用t-sne這一展示高維數(shù)據(jù)的技術來可視化輸出結果。在最后,我們將會使用遷移學習技術。我們會使用預先建立好的詞向量模型glove到我們的神經(jīng)網(wǎng)絡里來分類句子。雖然有非常多的深度學習的框架,例如TensorFlow、Kera、Torch和Caffe,但我們會使用Apache MXNet。因為MXNet的靈活性和跨多GPU的擴展性,它正變得流行起來。

我強烈建議你下載這些Notebook文件。其中有我寫好和運行通過的代碼。去嘗試它們!調整一些超參數(shù),對神經(jīng)網(wǎng)絡架構或是數(shù)據(jù)準備過程試驗不同的方法,看看你能否在準確度上超過我。

Notebook文件里面的內容要求你對卷積運算、神經(jīng)網(wǎng)絡、激活單元、梯度下降和Numpy庫有基本的理解。

在學習完本文的內容后,你將能夠:

1.理解情感分析的復雜性。

2.理解詞向量和它的應用。

3.為訓練神經(jīng)網(wǎng)絡準備數(shù)據(jù)集。

4.為使用不同的模型進行情感分類來實現(xiàn)神經(jīng)網(wǎng)絡架構的定制。

5.使用t-sne來可視化輸出的結果。

6.憑借已有的詞向量(如glove)來用有限的數(shù)據(jù)(小數(shù)據(jù)集或是短句子)進行訓練。

情感分析

情感分析是一個復雜的任務。理解一個句子是表達的正面還是負面觀點是非常困難的。例如這個句子,“這個電影夠無腦、無厘頭的,還相當?shù)猛?,但是我喜歡?!北M管這個句子里包含了好幾個負面的詞語(無腦、無厘頭和土),但這個句子卻表達的是正面的觀點(“但是我喜歡”)。另外一個不能僅憑單個詞語來簡單理解的句子是:“這部電影并不關心任何的聰明、智慧和任何類型的智能?!彪m然句子里包含了不少的正面詞匯(聰明、智慧和智能),但它是一個負面的評論。

一些句子是帶有嘲諷性的,或者它們的意思取決于上下文。比如“《颶風營救3》讓《颶風營救2》成為一部佳作”。我們能理解這句話其實是貶義,因為我們知道它的上下文(《颶風營救2》并不是一部好片子)。

由于上下文的關系,情感分析是一項非常困難的任務。它的準確性很多時候依賴于要處理的數(shù)據(jù)。一個情感分析器可能在一套數(shù)據(jù)上表現(xiàn)得很好,但在另外一套上就表現(xiàn)很糟。機器學習的開發(fā)人員應該意識到這一點,并檢查他們的模型是否能捕獲到數(shù)據(jù)的變化從而避免尷尬的失敗。

編碼數(shù)據(jù)集

越遠離表格化數(shù)據(jù),數(shù)據(jù)的處理就越復雜。相比于文字,即使是把圖像編碼成數(shù)字這樣的工作都是一個相對簡單直接的過程。每個像素只能在RGB色彩空間的0-255之內取值,這些值被存儲在一個二維的數(shù)組里。對一張圖片重設尺寸并不會影響其內容,因此我們可以相對容易地(基于圖像處理專家的工作之上)把圖像標準化輸出為可比較的數(shù)組。

但是,把自然語言(詞匯)編碼成數(shù)字就不那么容易了。一種語言可以有海量的詞匯,用這些詞匯形成的句子就能有各種變化。有時,重設句子的尺寸就會完全改變它們的意思。

讓我們看一個樣例來幫助理解編碼自然語言的過程??纯催@組詞{我、愛、蛋糕、不喜歡、披薩}({I、love、cake、hate、pizza})。它們構成了我們整個的詞匯表。下面兩個句子就是用這個詞匯表里面的詞匯構成的:“我愛蛋糕”(I love cake),“我不喜歡披薩”(I hate pizza)。

我們如何能把這些句子編碼成數(shù)字?一種方法是使用one-hot編碼法把這個詞匯表表示成一個如下所示的編碼矩陣。

vocab-6458d38e6bd3e30193745422b5b95594

在這個表示方法里,如果詞匯表里有N個詞,我們就需要一個N×N的矩陣。這個矩陣被叫做詞匯表矩陣。這個詞匯表矩陣就是查找詞匯的表。

現(xiàn)在,讓我們試著編碼句子?!拔也幌矚g披薩”將會成為下面這個矩陣。

sentence-dc42c2b94b422e6c36060ed09f8725c5

如果一個詞出現(xiàn)在句子里,相應的詞匯的行就被從詞匯表矩陣里復制過來。這就和在MXNet(或是其他的深度學習框架)的神經(jīng)網(wǎng)絡里的向量層所做的操作是一樣的。這一“向量層”就僅僅是進行查找操作,請不要和“詞向量”搞混了。關于詞向量,我們很快就會介紹的。

有比one-hot編碼詞匯表更好的方法嗎?有更好的表征詞的方式嗎?詞向量就能解決這個問題。與離散化詞匯不同,詞向量對詞匯提供了一個連續(xù)值的表征。一個詞向量矩陣可以像下面這個樣子。

embedding-585f5bc2909bde94fdd5081b3c3c6fd1-1

與使用N×N矩陣的詞匯表示法不同,我們這里使用一個N×3的矩陣來表征詞匯。這里的3就是向量的長度。因此,每個詞被表示成了一個3維的向量,而不是N維向量。

詞向量不僅能降低詞匯表矩陣的尺寸,還能夠編碼詞和詞之間的語義關系。例如,“披薩”和“蛋糕”在詞匯表里有相似的向量表示,因為它們二者都是食物?!跋矏邸焙汀安幌矚g”在第二維上有相同的值,因為它們都表達了感受;但在第一維度上卻截然不同(分別是0.9和0.1),因為它們表示的是相反的情感。

這些向量是哪里來的?這些詞向量可以是由你的深度神經(jīng)網(wǎng)絡在自動進行情感分類過重中學習到的。特定詞的詞向量可以被理解成你的深度神經(jīng)網(wǎng)絡需要去學習的權重。這些向量技術也可以被用于圖片和其他類型的數(shù)據(jù);它們通常被認為是自編碼器網(wǎng)絡。通常,自編碼器試圖去用一個更低維的空間來表征輸入內容,同時保證最小的信息損失。關于自編碼器的詳細解釋可以在這里找到。

句子的卷積

當用向量層把句子編碼成矩陣后,我們就可以對編碼矩陣進行一維卷積。這和對兩維的圖像進行兩維卷積很類似。卷積濾波器的尺寸取決于我們想要使用的n-gram。

embedding-585f5bc2909bde94fdd5081b3c3c6fd1

在邊緣的卷積運算會假定輸入的邊緣是零。這一卷積運算將會幫你基于n-gram從句子里學習。

下面,我們用一個真實的電影評論數(shù)據(jù)集來看看如何使用MXNet來進行情感分類。

準備你的環(huán)境

如果你在使用AWS云服務,你可以使用一個Amazon Machine Image鏡像文件來節(jié)省安裝的工作。這是一個預先配置好的用于深度學習的鏡像。使用它可以讓你忽略下面的1-5步。

需要注意的是,如果你使用Conda的環(huán)境,在啟動一個環(huán)境后,請記得使用conda install pip命令在conda里安裝pip。這么做會為你后面的操作免掉很多的麻煩。

下面是如何設置環(huán)境:

1.首先是獲取Anaconda,這是一個包管理器。它將幫助你輕松地安裝各種Python的依賴庫。

2.接下來安裝一個通用的科學計算庫scikit learn。我們將使用它來預處理我們的數(shù)據(jù)。你可以使用?conda install scikit-learn命令來安裝它。

3.然后使用conda install jupyter notebook命令安裝Jupyter Notebook。

4.下面是MXNet,一個開源的深度學習庫。

接下來的三步是實現(xiàn)詞向量的可視化所需要的。它們不是必須要執(zhí)行的,但是我強烈建議你可視化結果。

1.下面我們需要安裝cython,這是bhtnse庫所需要的。

2.我們還需要btsne,這是一個用c++寫的tnse的python實現(xiàn)。不要使用scikit-learn自己的tnse實現(xiàn),它會讓python的內核崩潰。

3.最后,我們需要安裝matplotlib來畫圖和進行可視化。

下面是在啟動一個anaconda環(huán)境后,我們需要在這個環(huán)境里使用的安裝命令。

1.conda install pip

2.pip install opencv-python

3.conda install scikit-learn

4.conda install jupyter notebook

5.pip install mxnet

6.conda install -c anaconda cython

7.pip install bhtsne

8.conda install -c anaconda matplotlib

數(shù)據(jù)集

為了學習深度神經(jīng)網(wǎng)絡,我們需要數(shù)據(jù)。對本文的任務而言,我們將會使用來自Stanford的電影評論數(shù)據(jù)。你可以在這里下載這個數(shù)據(jù)集。

這個數(shù)據(jù)集包含25000個訓練樣本的訓練集和25000個測試樣本的測試集。每個數(shù)據(jù)集里都有一半是正面情感,一半是負面情感。出于速度和簡單化的考慮,我們將只使用訓練集。我們把它隨機地分成了訓練、驗證和測試三個部分。但就如前面所說的,情感分析是一個復雜的任務,它的準確性會依賴于數(shù)據(jù)集本身。因此你的模型應該在多個數(shù)據(jù)集上被測試通過后才能部署到生產(chǎn)系統(tǒng)上去。

這里是加載數(shù)據(jù)的代碼。 我們?yōu)檎媲榫w分配標簽1,為負面情緒分配標簽0。 因為我們將使用MXnet中的softmaxoutput層來執(zhí)行分類,所以我們不會對單個標簽進行one-hot編碼。

在另一個深度學習框架中,對標簽進行one-hot編碼可能是必要的。
import os
def read_files(foldername):
import os
sentiments = []
filenames = os.listdir(os.curdir+ “/”+foldername)
for file in filenames:
with open(foldername+”/”+file,”r”, encoding=”utf8″) as pos_file:
data=pos_file.read().replace(‘\n’, ”)
sentiments.append(data)
return sentiments

#contains positive movie review
foldername = “easy/pos”
postive_sentiment = read_files(foldername)

#contains negative movie review
foldername = “easy/neg”
negative_sentiment = read_files(foldername)

positive_labels = [1 for _ in postive_sentiment]
negative_labels = [0 for _ in negative_sentiment]

準備數(shù)據(jù)集和編碼

我們需要從標準的數(shù)據(jù)準備任務開始。需要進行刪除URL、特殊字符等操作來清理數(shù)據(jù)。你還可以使用nltk庫來預處理文本。在我們這個場景里,我們使用自定義函數(shù)來清理數(shù)據(jù)。一旦數(shù)據(jù)被清除,我們需要形成詞匯表(數(shù)據(jù)集中所有可用的唯一詞)。

接下來,我們需要找到評論中最常用的單詞。這可以防止諸如director name或actor name這樣的相對罕見的詞語影響分類器的結果。我們還將每個單詞(根據(jù)出現(xiàn)頻率降序排列)映射到一個名為word_dict的字典中的唯一編號(idx)。我們也會有從idx到單詞的逆映射。我們這里使用一個5000字的詞匯表,也就是說,我們只把最常見的5000個單詞視為重要的。詞匯表的大小、句子長度和向量尺寸也可以作為參數(shù),在構建神經(jīng)網(wǎng)絡的同時進行實驗。我們使用的詞匯量為5000,句子長度為500字,向量尺寸為50。
#some string preprocessing
def clean_str(string):
“””
Tokenization/string cleaning for all datasets except for SST.
Original taken from https://github.com/yoonkim/CNN_sentence/blob/master/process_data.py
“””
string = re.sub(r”[^A-Za-z0-9(),!?\’\`]”, ” “, string)
string = re.sub(r”\’s”, ” \’s”, string)
string = re.sub(r”\’ve”, ” \’ve”, string)
string = re.sub(r”n\’t”, ” n\’t”, string)
string = re.sub(r”\’re”, ” \’re”, string)
string = re.sub(r”\’d”, ” \’d”, string)
string = re.sub(r”\’ll”, ” \’ll”, string)
string = re.sub(r”,”, ” “, string)
string = re.sub(r”!”, ” ! “, string)
string = re.sub(r”\(“, ” “, string)
string = re.sub(r”\)”, ” “, string)
string = re.sub(r”\?”, ” \? “, string)
string = re.sub(r”\s{2,}”, ” “, string)
string = re.sub(r”,”, ” “, string)
string = re.sub(r”‘”, ” “, string)
string = re.sub(r”\[“, ” “, string)
string = re.sub(r”\]”, ” “, string)
return string.strip().lower()

#Creating a dict of words and their count in the entire dataset{word:count}
word_counter = Counter()
def create_count(sentiments):
idx = 0
for line in sentiments:
for word in (clean_str(line)).split():
if word not in word_counter.keys():
word_counter[word] = 1
else:
word_counter[word] += 1

#Assigns a unique a number for each word (sorted by descending order based on the frequency of occurrence)
#and returns a word_dict
def create_word_index():
idx = 0
word_dict = {}
for word in word_counter.most_common():
word_dict[word[0]] = idx
idx+=1
return word_dict

#inverse mapping
idx2word = {v: k for k, v in word_dict.items()}

接下來,我們使用word_dict將這些句子編碼成數(shù)字。以下代碼執(zhí)行這一操作。
#Creates encoded sentences.
#Assigns the unique id from wordict to the words in the sentences
def encoded_sentences(input_file,word_dict):
output_string = []
for line in input_file:
output_line = []
for word in (clean_str(line)).split():
if word in word_dict:
output_line.append(word_dict[word])
output_string.append(output_line)
return output_string

def decode_sentences(input_file,word_dict):
output_string = []
for line in input_file:
output_line = ”
for idx in line:
output_line += idx2word[idx] + ‘ ‘
output_string.append(output_line)
return output_string

接下來,我們填充或截斷一個句子,把它的長度定為500字。也可以根據(jù)句子字數(shù)的統(tǒng)計來選擇不同的長度。

def pad_sequences(sentences,maxlen=500,value=0):
“””
Pads all sentences to the same length. The length is defined by the longest sentence.
Returns padded sentences.
“””
padded_sentences = []
for sen in sentences:
new_sentence = []
if(len(sen) > maxlen):
new_sentence = sen[:maxlen]
padded_sentences.append(new_sentence)
else:
num_padding = maxlen – len(sen)
new_sentence = np.append(sen,[value] * num_padding)
padded_sentences.append(new_sentence)
return padded_sentences

下面,將數(shù)據(jù)分成訓練、驗證和測試集:
#train+validation, test split
X_train_val, X_test, y_train_val, y_test_set = train_test_split(t_data, all_labels, test_size=0.3, random_state=42)

#train, validation split of data
X_train, X_val, y_train, y_validation = train_test_split(X_train_val, y_train_val, test_size=0.3, random_state=42)

構建deepnet

現(xiàn)在我們已經(jīng)準備好了數(shù)據(jù)集,讓我們編寫神經(jīng)網(wǎng)絡。構建神經(jīng)網(wǎng)絡就像是科學研究——它涉及到很多實驗。 我們可以選擇自己做實驗或使用其他研究人員用來解決類似問題的現(xiàn)成的神經(jīng)網(wǎng)絡。我們將從一個簡單的密集網(wǎng)絡開始,然后使用復雜的神經(jīng)網(wǎng)絡架構。

下面的神經(jīng)網(wǎng)絡的代碼非常簡單明了。這多虧了MXNet的符號化API:
#A simple dense model
input_x_1 = mx.sym.Variable(‘data’)
embed_layer_1 = mx.sym.Embedding(data=input_x_1, input_dim=vocab_size, output_dim=embedding_dim, name=’vocab_embed’)
flatten_1 = mx.sym.Flatten(data=embed_layer_1)
fc1_1 = mx.sym.FullyConnected(data=flatten_1, num_hidden=500,name=”fc1″)
relu3_1 = mx.sym.Activation(data=fc1_1, act_type=”relu” , name=”relu3″)
fc2_1 = mx.sym.FullyConnected(data=relu3_1, num_hidden=2,name=”final_fc”)
dense_1 = mx.sym.SoftmaxOutput(data=fc2_1, name=’softmax’)

讓我們把代碼分開了仔細看看。首先,它創(chuàng)建了一個數(shù)據(jù)層(輸入層)來存儲訓練期間用的數(shù)據(jù)集:
data = mx.symbol.Variable(‘data’)

vocab_embed層負責查詢向量矩陣(在這一過程中會被學習出來):
embed_layer_1 = mx.sym.Embedding(data=input_x_1, input_dim=vocab_size, output_dim=embedding_dim, name=’vocab_embed’)

這個扁平層把向量層所產(chǎn)生的(seq_len×向量維度)的權重矩陣轉換成一個只有一列的1×(seq_len×向量維度)的向量,作為下一密集層的輸入。
flatten_1 = mx.sym.Flatten(data=embed_layer_1)

全連接層把來自扁平層(上一層)的每個輸入都連接到當前層的每個神經(jīng)元上(輸出)。
mx.sym.FullyConnected(data=flatten_1, num_hidden=500,name=”fc1″)

relu3_1層對輸入進行一次非線性激活來學習復雜的功能。
relu3_1 = mx.sym.Activation(data=fc1_1, act_type=”relu” , name=”relu3″)

最終的密集層(softmax)完成分類。MXNet里的SoftmaxOutput層完成one-hot編碼的輸出,然后對它使用softmax函數(shù)。

訓練這個網(wǎng)絡

我們會使用GPU對網(wǎng)絡進行訓練,因為這能提高訓練速度,可以把訓練時間減少91%。 這將會提高開發(fā)速度和產(chǎn)品發(fā)布時間,也能降低開發(fā)成本。訓練集的一次使用被稱為一個“epoch”,我們用3個epoch(設“num_epoch = 3”)來訓練網(wǎng)絡。 我們還定期將訓練好的模型存儲在JSON文件中,并用它測量訓練和驗證的準確性,以查看我們的神經(jīng)網(wǎng)絡“學習”的如何了。

下面是代碼:
#Create Adam optimiser
adam = mx.optimizer.create(‘adam’)

#Checkpointing (saving the model). Make sure a folder named models exists.
model_prefix = ‘model1/chkpt’
checkpoint = mx.callback.do_checkpoint(model_prefix)

#Loading the module API. Previously MXNet used feedforward (deprecated)
model = mx.mod.Module(
context = mx.gpu(0), # use GPU 0 for training; if you don’t have a gpu use mx.cpu()
symbol = dense_1,
data_names=[‘data’]
)

#actually fits the model for 3 epochs.
model.fit(
train_iter,
eval_data=val_iter,
batch_end_callback = mx.callback.Speedometer(batch_size, 64),
num_epoch = 3,
eval_metric=’acc’,
optimizer = adam,
epoch_end_callback=checkpoint
)

可視化

下面的代碼將幫助我們對結果進行可視化,可以提供對于我們的模型的一些理解。我們可以獲得由我們的模型生成的向量的權重,然后使用tnse可視化展示結果。

以二維展示vocab-size * embedding_dim(5000 * 500)維的向量是不可能的。 我們需要降低數(shù)據(jù)的維度以便可視化。t-sne是一種流行的可視化技術,它可以降低維數(shù),使得在N(500)維中更接近的向量在較低維度(2)也靠近。它基本是把N維向量組合成一個簡化的維度,同時信息損失最小。 t-sne與PCA非常相似。

下面是提取向量權重,然后將它們可視化為散點圖的代碼:
# obtains the weights of embeding layer for visualizing
params = model.get_params()
print(params)
weights_dense_embed = model.get_params()[0][‘vocab_embed_weight’].asnumpy()
weights_dense_embed = weights_dense_embed.astype(‘float64′)

#tnse visualization for first 500 words
size = 500
Y= tsne(weights_dense_embed[:size])
plt.figure(0)
plt.scatter(Y[:, 0], Y[:, 1])
for idx, (x, y) in enumerate(zip(Y[:, 0], Y[:, 1])):
plt.annotate(idx2word[idx], xy=(x, y), xytext=(0, 0), textcoords=’offset points’)

下面的t-sne圖展示了前500個詞的權重。

embedding1-98731dc61a2f53969d16bd2fe3449f16

這個可視化的圖展示了模型自動學習到“excellent(精彩)、wonderful(完美)、amazing(神奇)”都是同一個意思。這相當?shù)貌诲e!下面我們寫一個簡單的預測函數(shù)來使用這個模型。

樣本預測

我們用這個模型來預測句子的情感。為此,我們需要加載保存的模型:
# Load the model from the checkpoint . We are loading the 10 epoch
sym, arg_params, aux_params = mx.model.load_checkpoint(model_prefix, 3)

# Assign the loaded parameters to the module
mod = mx.mod.Module(symbol=sym, context=mx.cpu())
mod.bind(for_training=False, data_shapes=[(‘data’, (1,500))])
mod.set_params(arg_params, aux_params)

接著用word_dict的索引來編碼要預測的句子,再把它傳給模型分類器。
from collections import namedtuple
Batch = namedtuple(‘Batch’, [‘data’])

#a simple predict function
def predict(sen):
# compute the predict probabilities
mod.forward(Batch([mx.nd.array(sen)]))
prob = mod.get_outputs()[0].asnumpy()
# print the top-5
prob = np.squeeze(prob)
return prob

#our custom sentences for testing
my_sen =[“the movie was awesome. Loved it”]
my_sen_encoded = encoded_sentences(my_sen,word_dict)
my_sen_encoded_padded = pad_sequences(my_sen_encoded)

輸出是[0.09290998 0.90709001],這意味著分類器以0.90的概率認為句子的情感是正面的。

然而,這個模型只用單個的詞做出預測。如果考慮到單詞之間的關系,我們可以做得更好。為了做到這一點,我們需要建立一個一次可以考慮多個連續(xù)的單詞(n-gram)的卷積神經(jīng)網(wǎng)絡。 接下來,我們將構建一個可以捕獲n-gram里的信息的卷積網(wǎng)絡,用于情感分類。

構建卷積模型

如前所述,為了開發(fā)一個基于n-gram的模型,我們需要一個卷積神經(jīng)網(wǎng)絡。 所以,我們構建一個。 這與之前的模型非常相似,只是卷積濾波器的尺寸變成了5。
#a model with convolution kernel of 5 (5-grams)
input_x_2 = mx.sym.Variable(‘data’)
embed_layer_2 = mx.sym.Embedding(data=input_x_2, input_dim=vocab_size, output_dim=embedding_dim, name=’vocab_embed’)
conv_input_2 = mx.sym.Reshape(data=embed_layer_2, target_shape=(batch_size, 1, seq_len, embedding_dim))
conv1_2 = mx.sym.Convolution(data=conv_input_2, kernel=(5,embedding_dim), num_filter=100, name=”conv1″)
flatten_2 = mx.sym.Flatten(data=conv1_2)
fc2_2 = mx.sym.FullyConnected(data=flatten_2, num_hidden=2,name=”final_fc”)
convnet = mx.sym.SoftmaxOutput(data=fc2_2, name=’softmax’)

唯一棘手的是conv_input_2輸入層。該層將向量層的輸出重新整形為卷積層所需的格式。模型的其他一切都保持不變??梢允褂胢odel.fit函數(shù)來訓練該模型,并且可以獲得各種見解。

構建“多卷積”模型

這個模型和之前的一樣,除了我們使用了多個卷積的尺寸(3,4,5)來構建3-gram、4-gram和5-gram的模型,然后對輸出進行了裁剪和扁平化。一個最大池化層被加入進模型來防止過擬合。下面是python代碼:
# a model with convolution filters of 3, 4, 5 (3-gram,4-grams,5-grams)
input_x_3= mx.sym.Variable(‘data’)
embed_layer_3 = mx.sym.Embedding(data=input_x_3, input_dim=vocab_size, output_dim=embedding_dim, name=’vocab_embed’)
conv_input_3 = mx.sym.Reshape(data=embed_layer_3, target_shape=(batch_size, 1, seq_len, embedding_dim))

# create convolution + (max) pooling layer for each filter operation
filter_list=[3, 4, 5] # the size of filters to use

num_filter=100
pooled_outputs = []
for i, filter_size in enumerate(filter_list):
convi = mx.sym.Convolution(data=conv_input_3, kernel=(filter_size, embedding_dim), num_filter=num_filter)
relui = mx.sym.Activation(data=convi, act_type=’relu’)
pooli = mx.sym.Pooling(data=relui, pool_type=’max’, kernel=(seq_len – filter_size + 1, 1), stride=(1,1))
pooled_outputs.append(pooli)

# combine all pooled outputs
total_filters = num_filter * len(filter_list)
concat = mx.sym.Concat(*pooled_outputs, dim=1)

# reshape for next layer
h_pool_3 = mx.sym.Reshape(data=concat, target_shape=(batch_size, total_filters))
fc2_3 = mx.sym.FullyConnected(data=h_pool_3, num_hidden=2,name=”final_fc”)
convnet_combined = mx.sym.SoftmaxOutput(data=fc2_3, name=’softmax’)

使用glove的遷移學習

我們使用神經(jīng)網(wǎng)絡生成的詞向量給了我們很多見解。但是為了生成詞向量,我們需要大量的數(shù)據(jù)。如果我們只有少量的數(shù)據(jù)呢?從不同的預先訓練的神經(jīng)網(wǎng)絡傳遞權重可能是非常有用的。這有助于在即使只有少量的數(shù)據(jù)的情況下建立一個模型。

在這里,我們將使用Stanford大學開發(fā)的glove詞向量。我們使用維基百科-2014年glove詞向量,它是在60億詞的維基百科語料庫上訓練的。詞向量本身具有40萬個唯一的單詞(詞匯),并包含各種維度尺寸(50,100,200,300)。我們將使用50維詞向量來訓練神經(jīng)網(wǎng)絡。詞向量維度這個值也是一個超參數(shù),你應該嘗試不同的值,看看哪一個給你最好的結果。以下函數(shù)將詞向量加載到向量矩陣(一個numPy矩陣)中:
#loads glove word embedding
def load_glove_index(loc):
f = open(loc,encoding=”utf8″)
embeddings_index = {}
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype=’float32′)
embeddings_index[word] = coefs
f.close()
return embeddings_index

#creates word embedding matrix
def create_emb():
embedding_matrix = np.zeros((vocab_size, embedding_dim))
for word, i in word_dict.items():
if i >= vocab_size:
continue
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words not found in embedding index will be all-zeros.
embedding_matrix[i] = embedding_vector
return embedding_matrix
embeddings_index = load_glove_index(“glove/” + ‘glove.6B.50d.txt’)
embedding_matrix = create_emb();

讓我們用t-sne來可視化一下glove的詞向量:
#visualization of word embedding.
size = 500
Y= tsne(embedding_matrix[:size])
plt.figure(1)
plt.scatter(Y[:, 0], Y[:, 1])
for idx, (x, y) in enumerate(zip(Y[:, 0], Y[:, 1])):
plt.annotate(idx2word[idx], xy=(x, y), xytext=(0, 0), textcoords=’offset points’)

下圖是對glove的頭500個詞進行t-sne可視化的結果:

embedding2-8a16d4997cf073a3a9c08ac886365c0d

正如你所看到的那樣,詞向量已經(jīng)將類似的單詞“worse”(比較差),“worst”(最差),“bad”(差),“terrible”(恐怖)分組在一起。 讓我們用這個詞向量來創(chuàng)建一個神經(jīng)網(wǎng)絡進行句子情感分類。將預先訓練的權重添加到神經(jīng)網(wǎng)絡可能有點棘手。以下是代碼:
#creates a model with convolution and pre-trained word embedding.
weight_matrix = mx.nd.array(embedding_matrix)
input_x_3= mx.sym.Variable(‘data’)
the_emb_3 = mx.sym.Variable(‘weights’) #the weight variable which will hold the pre trained embedding matrix
embed_layer_3 = mx.sym.Embedding(data=input_x_3,weight=the_emb_3, input_dim=vocab_size, output_dim=embedding_dim, name=’vocab_embed’)
conv_input_3 = mx.sym.Reshape(data=embed_layer_3, target_shape=(batch_size, 1, seq_len, embedding_dim))
conv1_3 = mx.sym.Convolution(data=conv_input_3, kernel=(5,embedding_dim), num_filter=100, name=”conv1″)
flatten_3 = mx.sym.Flatten(data=conv1_3)
fc2_3 = mx.sym.FullyConnected(data=flatten_3, num_hidden=2,name=”final_fc”)
convnet_word2vec = mx.sym.SoftmaxOutput(data=fc2_3, name=’softmax’)

在這里,我們首先將glove的embedding_matrix轉換成mx.nd.array。 然后我們創(chuàng)建一個名為權重的符號變量,并將其賦值給變量the_emb_3。該變量作為參數(shù)傳遞給mx.sym.Embedding中的embed_layer_3。

下一步是通過將weight_matrix作為embed_layer_3的默認權重來訓練神經(jīng)網(wǎng)絡。此外,我們需要凍結詞向量的權重,以便在反向傳播時,embed_matrix的權重不會被更新。記得在模塊API中傳入fixed_param_names =[‘weights’]以凍結詞向量層的權重。此外,在使用glove詞向量權重進行擬合時,傳入arg_params={‘weights’: weight_matrix}。 下面是python代碼:
adam = mx.optimizer.create(‘adam’)

#Checkpointing (saving the model). Make sure there is folder named model4 exist
model_prefix_3 = ‘model4/chkpt’
checkpoint = mx.callback.do_checkpoint(model_prefix_3)

#Loading the module API. Previously MXNet used feed forward (deprecated)
model_3 = mx.mod.Module(
context = mx.gpu(0), # use GPU 0 for training; if you don’t have a gpu use mx.cpu()
symbol = convnet_word2vec,
fixed_param_names =[‘weights’] # makes the weights variable non trainable. Back propagration will not update #this variable
)

#fits the model for 5 epochs.
model_3.fit(
train_iter,
eval_data=val_iter,
batch_end_callback = mx.callback.Speedometer(batch_size, 64),
num_epoch = 5,
eval_metric=’acc’,
optimizer = adam,
epoch_end_callback=checkpoint,
arg_params={‘weights’: weight_matrix}, #loads the pretrained glove embedding to weights variable
allow_missing= True

您可能已經(jīng)注意到,對于這個特定的數(shù)據(jù)集使用glove詞向量(預先訓練的詞向量)并不能帶來更好的結果。試驗不同維度的詞向量和使用更高容量的模型(更多的神經(jīng)元)可能會提高性能。在這里下載Notebook文件,看看你是否能讓它工作得更好! 您也可以考慮使用像LTSMGRU這樣的遞歸神經(jīng)網(wǎng)絡來提高表現(xiàn)

結論

在本教程中,我們對電影評論數(shù)據(jù)集進行了情感分類。 我們使用MXNet開發(fā)了不同復雜度的模型,并理解了詞向量這一重要概念,學習了如何可視化我們模型學習的權重的方法。最后,我們還進行了使用glove詞向量的遷移學習。

在后續(xù)教程中,我們將學習如何使用深度學習來生成新的圖像、聲音等。這些類型的模型被稱為生成模型。

這篇博文是O’Reilly和Amazon的合作產(chǎn)物。請閱讀我們的編輯獨立聲明。

Manu Jeevan
Manu Jeevan是人工智能公司AI Solutions的聯(lián)合創(chuàng)始人,該公司為企業(yè)構建人工智能解決方案。他是一名自學成才的數(shù)據(jù)科學家,喜歡用人工智能和機器學習來解決復雜的業(yè)務問題。 可以在LinkedIn上找到他。

Suresh Rathnaraj
Suresh Rathnaraj是人工智能公司AI Solutions的聯(lián)合創(chuàng)始人,該公司為企業(yè)構建AI解決方案。 他是一名機器學習工程師,擁有計算機科學碩士學位。 他非常熱衷于深度學習,并認為它可以徹底改變整個科技行業(yè)。 業(yè)余時間里,他會打橄欖球并練習瑜伽??梢栽贚inkedIn上找到他。

Letters (source: Pixabay)