神经网络训练一个文本分类器

栏目: 数据库 · 发布时间: 5年前

内容简介:理解聊天机器人的工作原理是非常重要的。聊天机器人内部一个非常重要的组件就是文本分类器。我们看一下文本分类器的神经网络(ANN)的内部工作原理。多层神经网络
编辑推荐:

本文来自于machinelearnings。 文章主要引用需要的库,提供训练集,整理数据,迭代:编写代码+测试预测结果+调整模型,抽象,等方面介绍的。

理解聊天机器人的工作原理是非常重要的。聊天机器人内部一个非常重要的组件就是文本分类器。我们看一下文本分类器的神经网络(ANN)的内部工作原理。

神经网络训练一个文本分类器

多层神经网络

我们将会使用2层网络(1个隐层)和一个“词包”的方法来组织我们的训练数据。文本分类有3个特点:模式匹配、算法、神经网络。虽然使用多项朴素贝叶斯算法的方法非常有效,但是它有3个致命的缺陷:

这个算法输出一个分数而不是一个概率。我们可以使用概率来忽略特定阈值以下的预测结果。这类似于忽略收音机中的噪声。

这个算法从一个样本中学习一个分类中包含什么,而不是一个分类中不包含什么。一个分类中不包含什么的的学习模式往往也很重要。

不成比例的大训练集的分类将会导致扭曲的分类分数,迫使算法相对于分类规模来调整输出分数,这并不理想。

和它“天真”的对手一样,这种分类器并不试图去理解句子的含义,而仅仅对它进行分类。事实上,所谓的“人工智能聊天机器人”并不理解语言,但那是另一个故事。

如果你刚接触人工神经网络,这是它的工作原理。

理解分类算法,请看这里。

我们来逐个分析文本分类器的每个部分。我们将按照以下顺序:

引用需要的库

提供训练集

整理数据

迭代:编写代码+测试预测结果+调整模型

抽象

代码在这里,我们使用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?"})

print ("%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))

print (len(documents), "documents")

print (len(classes), "classes", classes)

print (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]

print ([stemmer.stem(word.lower()) for word in w])

print (training[i])

print (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:

print ("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:

print ("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

print ("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]

print ("%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?")

print()

classify("good day", show_details=True)

sudo make me a sandwich

[['sandwich', 0.99917711814437993]]

how are you today?

[['greeting', 0.99864563257858363]]

talk to you tomorrow

[['goodbye', 0.95647479275905511]]

who are you?

[['greeting', 0.8964283843977312]]

make me some lunch

[['sandwich', 0.95371924052636048]]

how was your lunch today?

[['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]]

现在你已经掌握了构建聊天机器人的一些基础知识结构,它能处理大量不同的意图,并且对于有限或者海量的训练数据都能很好的适配。想要为某个意图添加一个或者多个响应实在轻而易举,就不必多讲了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大话设计模式

大话设计模式

程杰 / 清华大学出版社 / 2007-12-1 / 45.00元

本书通篇都是以情景对话的形式,用多个小故事或编程示例来组织讲解GOF(设计模式的经典名著——Design Patterns:Elements of Reusable Object-Oriented Software,中译本名为《设计模式——可复用面向对象软件的基础》的四位作者EIich Gamma、Richard Helm、Ralph Johnson,以及John Vlissides,这四人常被称......一起来看看 《大话设计模式》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具