用神經網路訓練一個文本分類器
(點擊
上方藍字
,快速關注我們)
編譯:伯樂在線 - Anne90
如有好文章投稿,請點擊 → 這裡了解詳情
理解聊天機器人的工作原理是非常重要的。聊天機器人內部一個非常重要的組件就是文本分類器。我們看一下文本分類器的神經網路(ANN)的內部工作原理。
多層神經網路
我們將會使用2層網路(1個隱層)和一個「詞包」的方法來組織我們的訓練數據。文本分類有3個特點:模式匹配、演算法、神經網路。雖然使用多項樸素貝葉斯演算法的方法非常有效,但是它有3個致命的缺陷:
這個演算法輸出一個分數而不是一個概率。我們可以使用概率來忽略特定閾值以下的預測結果。這類似於忽略收音機中的雜訊。
這個演算法從一個樣本中學習一個分類中包含什麼,而不是一個分類中不包含什麼。一個分類中不包含什麼的的學習模式往往也很重要。
不成比例的大訓練集的分類將會導致扭曲的分類分數,迫使演算法相對於分類規模來調整輸出分數,這並不理想。
和它「天真」的對手一樣,這種分類器並不試圖去理解句子的含義,而僅僅對它進行分類。事實上,所謂的「人工智慧聊天機器人」並不理解語言,但那是另一個故事。
如果你剛接觸人工神經網路,這是它的工作原理(https://medium.com/@gk_/how-neural-networks-work-ff4c7ad371f7)。
理解分類演算法,請看這裡(https://chatbotslife.com/text-classification-using-algorithms-e4d50dcba45)。
我們來逐個分析文本分類器的每個部分。我們將按照以下順序:
引用需要的庫
提供訓練集
整理數據
迭代:編寫代碼+測試預測結果+調整模型
抽象
代碼在這裡,我們使用ipython notebook這個在數據科學項目上非常高效的工具。代碼語法是python。
我們首先導入自然語言工具包。我們需要一個可靠的方法將句子切分成詞並且將單詞詞幹化處理。
# use natural language toolkit
import nltk
from
nltk
.
stem
.
lancaster import LancasterStemmer
import os
import json
import datetime
stemmer
=
LancasterStemmer
()
下面是我們的訓練集,12個句子屬於3個類別(「意圖」)。
# 3 classes of training data
training_data
=
[]
training_data
.
append
({
"class"
:
"greeting"
,
"sentence"
:
"how are you?"
})
training_data
.
append
({
"class"
:
"greeting"
,
"sentence"
:
"how is your day?"
})
training_data
.
append
({
"class"
:
"greeting"
,
"sentence"
:
"good day"
})
training_data
.
append
({
"class"
:
"greeting"
,
"sentence"
:
"how is it going today?"
})
training_data
.
append
({
"class"
:
"goodbye"
,
"sentence"
:
"have a nice day"
})
training_data
.
append
({
"class"
:
"goodbye"
,
"sentence"
:
"see you later"
})
training_data
.
append
({
"class"
:
"goodbye"
,
"sentence"
:
"have a nice day"
})
training_data
.
append
({
"class"
:
"goodbye"
,
"sentence"
:
"talk to you soon"
})
training_data
.
append
({
"class"
:
"sandwich"
,
"sentence"
:
"make me a sandwich"
})
training_data
.
append
({
"class"
:
"sandwich"
,
"sentence"
:
"can you make a sandwich?"
})
training_data
.
append
({
"class"
:
"sandwich"
,
"sentence"
:
"having a sandwich today?"
})
training_data
.
append
({
"class"
:
"sandwich"
,
"sentence"
:
"what"s for lunch?"
})
(
"%s sentences in training data"
%
len
(
training_data
))
12 sentences in training data
現在我們可以將數據結構組織為:documents, classes 和words.
words
=
[]
classes
=
[]
documents
=
[]
ignore_words
=
[
"?"
]
# loop through each sentence in our training data
for
pattern
in
training_data
:
# tokenize each word in the sentence
w
=
nltk
.
word_tokenize
(
pattern
[
"sentence"
])
# add to our words list
words
.
extend
(
w
)
# add to documents in our corpus
documents
.
append
((
w
,
pattern
[
"class"
]))
# add to our classes list
if
pattern
[
"class"
]
not
in
classes
:
classes
.
append
(
pattern
[
"class"
])
# stem and lower each word and remove duplicates
words
=
[
stemmer
.
stem
(
w
.
lower
())
for
w
in
words
if
w
not
in
ignore_words
]
words
=
list
(
set
(
words
))
# remove duplicates
classes
=
list
(
set
(
classes
))
(
len
(
documents
),
"documents"
)
(
len
(
classes
),
"classes"
,
classes
)
(
len
(
words
),
"unique stemmed words"
,
words
)
12
documents
3
classes
[
"greeting"
,
"goodbye"
,
"sandwich"
]
26
unique stemmed
words
[
"sandwich"
,
"hav"
,
"a"
,
"how"
,
"for"
,
"ar"
,
"good"
,
"mak"
,
"me"
,
"it"
,
"day"
,
"soon"
,
"nic"
,
"lat"
,
"going"
,
"you"
,
"today"
,
"can"
,
"lunch"
,
"is"
,
""s"
,
"see"
,
"to"
,
"talk"
,
"yo"
,
"what"
]
注意每個單詞都是詞根並且小寫。詞根有助於機器將「have」和「having」等同起來。同時我們也不關心大小寫。
我們將訓練集中的每個句子轉換為詞包。
# create our training data
training
=
[]
output
=
[]
# create an empty array for our output
output_empty
=
[
0
]
*
len
(
classes
)
# training set, bag of words for each sentence
for
doc
in
documents
:
# initialize our bag of words
bag
=
[]
# list of tokenized words for the pattern
pattern_words
=
doc
[
0
]
# stem each word
pattern_words
=
[
stemmer
.
stem
(
word
.
lower
())
for
word
in
pattern_words
]
# create our bag of words array
for
w
in
words
:
bag
.
append
(
1
)
if
w
in
pattern_words
else
bag
.
append
(
0
)
training
.
append
(
bag
)
# output is a "0" for each tag and "1" for current tag
output_row
=
list
(
output_empty
)
output_row
[
classes
.
index
(
doc
[
1
])]
=
1
output
.
append
(
output_row
)
# sample training/output
i
=
0
w
=
documents
[
i
][
0
]
([
stemmer
.
stem
(
word
.
lower
())
for
word
in
w
])
(
training
[
i
])
(
output
[
i
])
[
"how"
,
"ar"
,
"you"
,
"?"
]
[
0
,
0
,
0
,
1
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
[
1
,
0
,
0
]
上面的步驟是文本分類中的一個經典步驟:每個訓練句子被轉化為一個包含0和1的數組,而不是語料庫中包含獨特單詞的數組。
["how", "are", "you", "?"]
被詞幹化為:
["how", "ar", "you", "?"]
然後轉換為輸入詞包的形式:1代表單詞存在於詞包中(忽略問號?)
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
輸出:第一類
[1, 0, 0]
注意:一個句子可以有多個分類,也可以沒有。確保理解上面的內容,仔細閱讀代碼直到你理解它。
機器學習的第一步是要有乾淨的數據
接下來我們的學習2層神經網路的核心功能。
如果你是人工神經網路新手,這裡是它的工作原理
我們使用numpy,原因是它可以提供快速的矩陣乘法運算。
我們使用sigmoid函數對值進行歸一化,用其導數來衡量錯誤率。通過不斷迭代和調整,直到錯誤率低到一個可以接受的值。
下面我們也實現了bag-of-words函數,將輸入的一個句子轉化為一個包含0和1的數組。這就是轉換訓練數據,得到正確的轉換數據至關重要。
import numpy
as
np
import
time
# compute sigmoid nonlinearity
def sigmoid
(
x
)
:
output
=
1
/
(
1
+
np
.
exp
(
-
x
))
return
output
# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative
(
output
)
:
return
output*
(
1
-
output
)
def clean_up_sentence
(
sentence
)
:
# tokenize the pattern
sentence_words
=
nltk
.
word_tokenize
(
sentence
)
# stem each word
sentence_words
=
[
stemmer
.
stem
(
word
.
lower
())
for
word
in
sentence_words
]
return
sentence_words
# return bag of words array: 0 or 1 for each word in the bag that exists in the sentence
def bow
(
sentence
,
words
,
show_details
=
False
)
:
# tokenize the pattern
sentence_words
=
clean_up_sentence
(
sentence
)
# bag of words
bag
=
[
0
]
*
len
(
words
)
for
s
in
sentence_words
:
for
i
,
w
in
enumerate
(
words
)
:
if
w
==
s
:
bag
[
i
]
=
1
if
show_details
:
(
"found in bag: %s"
%
w
)
return
(
np
.
array
(
bag
))
def think
(
sentence
,
show_details
=
False
)
:
x
=
bow
(
sentence
.
lower
(),
words
,
show_details
)
if
show_details
:
(
"sentence:"
,
sentence
,
"n bow:"
,
x
)
# input layer is our bag of words
l0
=
x
# matrix multiplication of input and hidden layer
l1
=
sigmoid
(
np
.
dot
(
l0
,
synapse_0
))
# output layer
l2
=
sigmoid
(
np
.
dot
(
l1
,
synapse_1
))
return
l2
現在我們對神經網路訓練函數進行編碼,創造連接權重。別太激動,這主要是矩陣乘法——來自中學數學課堂。
我們現在準備去構建我們的神經網路模型,我們將連接權重保存為json文件。
你應該嘗試不同的「α」(梯度下降參數),看看它是如何影響錯誤率。此參數有助於錯誤調整,並找到最低錯誤率:
synapse_0 += alpha * synapse_0_weight_update
我們在隱藏層使用了20個神經元,你可以很容易地調整。這些參數將隨著於您的訓練數據規模的不同而不同,將錯誤率調整到低於10 ^ – 3是比較合理的。
X
=
np
.
array
(
training
)
y
=
np
.
array
(
output
)
start_time
=
time
.
time
()
train
(
X
,
y
,
hidden_neurons
=
20
,
alpha
=
0.1
,
epochs
=
100000
,
dropout
=
False
,
dropout_percent
=
0.2
)
elapsed_time
=
time
.
time
()
-
start_time
(
"processing time:"
,
elapsed_time
,
"seconds"
)
Training
with
20
neurons
,
alpha
:
0.1
,
dropout
:
False
Input
matrix
:
12x26
Output
matrix
:
1x3
delta
after
10000
iterations
:
0.0062613597435
delta
after
20000
iterations
:
0.00428296074919
delta
after
30000
iterations
:
0.00343930779307
delta
after
40000
iterations
:
0.00294648034566
delta
after
50000
iterations
:
0.00261467859609
delta
after
60000
iterations
:
0.00237219554105
delta
after
70000
iterations
:
0.00218521899378
delta
after
80000
iterations
:
0.00203547284581
delta
after
90000
iterations
:
0.00191211022401
delta
after
100000
iterations
:
0.00180823798397
saved synapses
to
:
synapses
.
json
processing
time
:
6.501226902008057
seconds
synapse.json文件中包含了全部的連接權重,這就是我們的模型。
一旦連接權重已經計算完成,對於分類來說只需要classify()函數了:大約15行代碼
備註:如果訓練集有變化,我們的模型需要重新計算。對於非常大的數據集,這需要較長的時間。
現在我們可以生成一個句子屬於一個或者多個分類的概率了。它的速度非常快,這是因為我們之前定義的think()函數中的點積運算。
# probability threshold
ERROR_THRESHOLD
=
0.2
# load our calculated synapse values
synapse_file
=
"synapses.json"
with open
(
synapse_file
)
as
data_file
:
synapse
=
json
.
load
(
data_file
)
synapse_0
=
np
.
asarray
(
synapse
[
"synapse0"
])
synapse_1
=
np
.
asarray
(
synapse
[
"synapse1"
])
def classify
(
sentence
,
show_details
=
False
)
:
results
=
think
(
sentence
,
show_details
)
results
=
[[
i
,
r
]
for
i
,
r
in
enumerate
(
results
)
if
r
>
ERROR
_
THRESHOLD
]
results
.
sort
(
key
=
lambda
x
:
x
[
1
],
reverse
=
True
)
return_results
=
[[
classes
[
r
[
0
]],
r
[
1
]]
for
r
in
results
]
(
"%s n classification: %s"
%
(
sentence
,
return_results
))
return
return_results
classify
(
"sudo make me a sandwich"
)
classify
(
"how are you today?"
)
classify
(
"talk to you tomorrow"
)
classify
(
"who are you?"
)
classify
(
"make me some lunch"
)
classify
(
"how was your lunch today?"
)
()
classify
(
"good day"
,
show_details
=
True
)
<
strong
>
sudo make
me
a
sandwich
</
strong
>
[[
"sandwich"
,
0.99917711814437993
]]
<
strong
>
how are you
today
?
</
strong
>
[[
"greeting"
,
0.99864563257858363
]]
<
strong
>
talk
to
you
tomorrow
</
strong
>
[[
"goodbye"
,
0.95647479275905511
]]
<
strong
>
who are
you
?
</
strong
>
[[
"greeting"
,
0.8964283843977312
]]
<
strong
>
make me some
lunch
</
strong
>
[[
"sandwich"
,
0.95371924052636048
]]
<
strong
>
how was your lunch
today
?
</
strong
>
[[
"greeting"
,
0.99120883810944971
],
[
"sandwich"
,
0.31626066870883057
]]
你可以用其它語句、不同概率來試驗幾次,也可以添加訓練數據來改進/擴展當前的模型。尤其注意用很少的訓練數據就得到穩定的預測結果。
有一些句子將會產生多個預測結果(高於閾值)。你需要給你的程序設定一個合適的閾值。並非所有的文本分類方案都是相同的:一些預測情況比其他預測需要更高的置信水平。
最後這個分類結果展示了一些內部的細節:
found
in
bag
:
good
found
in
bag
:
day
sentence
: **
good day*
*
bow
:
[
0
0
0
0
0
0
1
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
]
good
day
[[
"greeting"
,
0.99664077655648697
]]
從這個句子的詞包中可以看到,有兩個單詞和我們的詞庫是匹配的。同時我們的神經網路從這些 0 代表的非匹配詞語中學習了。
如果提供一個僅僅有一個常用單詞 『a』 被匹配的句子,那我們會得到一個低概率的分類結果A:
found
in
bag
:
a
sentence
: **
a
burrito
! **
bow
:
[
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
]
a
burrito
!
[[
"sandwich"
,
0.61776860634647834
]]
現在你已經掌握了構建聊天機器人的一些基礎知識結構,它能處理大量不同的意圖,並且對於有限或者海量的訓練數據都能很好的適配。想要為某個意圖添加一個或者多個響應實在輕而易舉,就不必多講了。
Enjoy!
看完本文有收穫?請轉發分享給更多人
關注「大數據與機器學習文摘」,成為Top 1%
※深入理解 Python 非同步編程(上)
※Python 爬蟲實踐:《戰狼2》豆瓣影評分析
※150 多個 ML、NLP 和 Python 相關的教程
TAG:Python開發者 |
※想免費用谷歌資源訓練神經網路?Colab 詳細使用教程
※教程 | 如何快速訓練免費的文本生成神經網路
※教程|如何快速訓練免費的文本生成神經網路
※Hinton膠囊網路後最新:用「在線蒸餾」訓練大規模分散式神經網路
※基礎類完整訓練
※杠鈴是一種核心訓練運動訓練器材,也是舉重所用器材
※Hinton膠囊網路後最新研究:用「在線蒸餾」訓練大規模分散式神經網路
※怎樣訓練一個人類
※北航博士生黃雷:標準化技術在訓練深度神經網路中的應用|分享總結
※如何用 Python 和機器學習訓練中文文本情感分類模型?
※當前訓練神經網路最快的方式:AdamW優化演算法+超級收斂
※俄軍唯一航母大修,飛行員咋訓練?俄媒:快去借個甲板用用
※經試驗最有用的基礎八種訓練方法 不做標題黨
※無需預訓練分類器,清華&曠視提出專用於目標檢測的骨幹網路DetNet
※二十三:神經網路訓練中的批量歸一化
※人工智慧使用深度學習訓練通用的對抗修補程序攻擊RCNN網路
※深度學習工程模板:簡化載入數據、構建網路、訓練模型和預測樣本的流程
※每天五分鐘深蹲訓練有用嗎?
※肌肉訓練:一起來用兩分鐘,豐個唇!
※腦血栓康復訓練有三個階段