Task6 神经网络基础
任务
- 前馈神经网络、网络层数、输入层、隐藏层、输出层、隐藏单元、激活函数的概念。
- 感知机相关;定义简单的几层网络(激活函数sigmoid),递归使用链式法则来实现反向传播。
- 激活函数的种类以及各自的提出背景、优缺点。(和线性模型对比,线性模型的局限性,去线性化)
- 深度学习中的正则化(参数范数惩罚:L1正则化、L2正则化;数据集增强;噪声添加;early stop;Dropout层)、正则化的介绍。
- 深度模型中的优化:参数初始化策略;自适应学习率算法(梯度下降、AdaGrad、RMSProp、Adam;优化算法的选择);batch norm层(提出背景、解决什么问题、层在训练和测试阶段的计算公式);layer norm层。
- FastText的原理。
- 利用FastText模型进行文本分类。
概念:
- 前馈神经网络
这是实际应用中最常见的神经网络类型。
第一层是输入,最后一层是输出,如果有多个隐藏层则称之为“深度”神经网络。他们计算出一系列改变样本相似性的变换。各层神经元的活动是前一层活动的非线性函数。
2.网络层数
神经网络主要由:输入层,隐藏层,输出层构成。
当隐藏层只有一层时,该网络为两层神经网络;
- 输入层:
输入层未做任何变换,可以不看做单独的一层;
实际中网络输入层的每个神经元代表了一个特征,输入层个数代表了分类标签的个数;
- 隐藏层
单个隐藏层的意义就是把输入数据的特征,抽象到另一个维度空间,来展现其更抽象化的特征;
多个隐藏层其实是对输入特征多层次的抽象,最终的目的就是为了更好的线性划分不同类型的数据;
隐藏层层数以及隐藏层神经元是由人工设定 ;补充:理论上隐藏层越多可以将特征划分的更清楚,但是会带来两个问题:
- 层数越多参数会爆炸式增多
- 到了一定层数再往深了增加隐藏层,分类效果的增强会越来越不明显
- 输出层
在做二分类的时候,如果采用sigmoid分类器,输出层的神经元个数为1个;如果采用softmax分类器输出层神经元个数为2个
- 隐藏单元
我们把网络中的每一个结点称之为一个神经元(单元),隐藏单元即为隐藏层中的神经元(单元);
其中,神经元:人工神经元(Artificial Neuron),简称神经元(Neuron),是构成神经网络的基本单元,其主要是模拟生物神经元的结构和特性,接受一组输入信号并产出输出。
- 激活函数
激活函数是用来加入非线性因素的,解决线性模型所不能解决的问题;
在神经网络中,我们可以经常看到对于某一个隐藏层的节点,该节点的激活之计算一般分为两步:
(1) 输入该节点的值后先进行一个线性变换,计算出值
(2)再进行一个非线性变换,也就是经过一个非线性激活函数
常用的激活函数包括:sigmoid函数、tanh函数、ReLU函数。
sigmoid函数:
- 该函数数将取值为(−∞,+∞) 的数映射到(0,1)之间,其公式以及函数图如下所示:
求导推导为:
- sigmoid存在几个缺点,所以被使用的频率较低
- 当值非常大或者非常小是sigmoid的导数会趋近为0,则会导致梯度消失
- 函数的输出不是以0位均值,不便于下一层的计算
- 当目标是解决一个二分类问题,可在输出层使用sigmoid函数进行二分类。
tanh函数
- 该函数数将取值为(−∞,+∞) 的数映射到(-1,1)之间,其公式以及函数图如下所示:
tanh函数的均值为0,弥补了sigmoid函数均值为0.5的缺点
tanh函数的求导推导为:
- tanh与sigmoid存在相同的问题,当值很大或者很小的时候会出现梯度消失的问题
ReLU函数
- ReLU函数又称为修正线性单元, 是一种分段线性函数,其弥补了sigmoid函数以及tanh函数的梯度消失问题。ReLU函数的公式以及图形如下:
ReLU函数求导为:
- ReLU的优点:1. 当输入大于0时,不存在梯度消失的问题2. 由于ReLU函数只有线性关系,所以计算速度要快很多
- Relu的缺点:当输入小于0时,梯度为0,会产生梯度消失问题。
- 感知机相关
感知机是二分类的线性分类模型,输入为实例的特征向量,输出为实例的类别(取+1和-1);
感知机目的在求一个可以将实例分开的超平面,为了求它,我们用到基于误分类的损失函数和梯度下降的优化策略。
感知机模型理解见:https://www.leiphone.com/news/201706/FEz9czQa8TMPGyqr.html
- 正则化
它的形式很简单,是在目标函数后额外附加一项,使其影响目标函数最优点的选取。这种方法叫做正则化方法。
L1正则化、L2正则化原理参考:https://blog.csdn.net/u012950413/article/details/80464318#L1_86
- 数据集增强
数据集增强的原因:一般而言,比较成功的神经网络需要大量的参数,许许多多的神经网路的参数都是数以百万计,而使得这些参数可以正确工作则需要大量的数据进行训练,而实际情况中数据并没有我们想象中的那么多
- 数据集增强的作用:
- 增加训练的数据量,提高模型的泛化能力
- 增加噪声数据,提升模型的鲁棒性
如何进行数据增强:
- 利用已有的数据比如翻转、平移或旋转,创造出更多的数据,来使得神经网络具有更好的泛化效果。
- 离线增强:直接对数据集进行处理,数据的数目会变成增强因子 * 原数据集的数目 ,这种方法常常用于数据集很小的时候
- 在线增强 : 这种增强的方法用于,获得 batch 数据之后,然后对这个 batch 的数据进行增强,如旋转、平移、翻折等相应的变化,由于有些数据集不能接受线性级别的增长,这种方法长用于大的数据集。
- Early stoping
Early stopping可以实现在指定位置停止训练,获得一个泛化能力较强的模型,其主要步骤如下:
- 将原始的训练数据集划分成训练集和验证集
- 只在训练集上进行训练,并每个一个周期计算模型在验证集上的误差,例如,每15次epoch(mini batch训练中的一个周期)
- 当模型在验证集上的误差比上一次训练结果差的时候停止训练
- 使用上一次迭代结果中的参数作为模型的最终参数
补充:在现实中,模型在验证集上的误差并不平滑,也就是模型在验证集上的表现可能短暂的变差之后有可能继续变好,所以早停法主要是训练时间和泛化错误之间的权衡。
- Dropout层
我们在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征
工作流程及使用参见:https://blog.csdn.net/program_developer/article/details/80737724
FastText
- 模型架构
fasttext算法是一种有监督的结构,它通过上下文预测标签即文本的类别
fasttext模型的输入是一个词/一句话/一段文本的序列,输出的是这个序列属于不用类别的概率。
在序列中的词和词组构成特征向量,特征向量通过线性变换映射到中间层,再由中间层映射到标签。
fasttext在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。
fasttext代码整体结构
- 利用FastText模型进行文本分类
注意:win无法直接用pip install fasttext安装fasttext,我首先在python的第三方安装包网站https://www.lfd.uci.edu/~gohlke/pythonlibs/#fasttext*
fasttext本地安装方法
下载fasttext‑0.8.22‑cp36‑cp36m‑win_amd64.whl后再本地安装
本地调用fasttext:
import fastText.FastText as ff
补充:实验进行文本分类的文件已事先分词、去停用词后的结果
- 首先读文件
def read_file(path):
with open(path, 'r', encoding="UTF-8") as f:
data = []
labels = []
for line in f:
data.append(line.split('\t')[0])
labels.append(line.split('\t')[1])
return data, labels
data, labels = read_file('E:/task6/merge.txt')
- 对数据进行预处理
fasttext训练数据是直接对文件进行读取,所以在训练前应当对数据进行处理,
fasttext读取的文件有固定格式:
第一列 | 第二列 | 第三列 |
---|---|---|
label前缀 | 标签值 | 编码后文本 |
本次实验文本处理后格式为:
__label__0 379 3 2 322 75 19
__label__0 1284 134 811 546 2508 57 20 48 233 20 43 17 2 20 47 173 1284 134
__label__1 116 29 812 139 79 4
__label__1 21 281 79 68
__label__0 49 32
实现代码如下所示:
def get_tokenizer_data(data):
'''
fasttext传入文本必须对其进行预处理和编码
'''
tokenizer = Tokenizer(num_words=None)
# 得到文本的字典
tokenizer.fit_on_texts(data)
# 将每个string的每个词转成数字
data = tokenizer.texts_to_sequences(data)
return data
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(data,
labels,
test_size = 0.2,
random_state=33)
# 在标签值前增加前缀
label_train = [('__label__' + i).replace('\n', '') for i in y_train]
# 向量化文本
tokenizer_train = get_tokenizer_data(X_train)
# 将label列与文本列合并为一行
train = [i + ' ' + str(j).replace('[', '').replace(']', '').replace(',', '') for i, j in zip(label_train, tokenizer_train)]
- 最后使用fasttext训练文本并预测
def fast_text_model(X_test):
'''
使用fasttext进行文本分类
'''
# 分类训练
classifier = ff.train_supervised('E:/task6/train.txt', label='__label__')
# 模型预测,返回预测标签和概率
label, prob = classifier.predict(X_test)
print(label)
print(prob)
# 根据给定数据集对模型进行评价,返回样本个数、准确率、召回率
result = classifier.test('E:/task6/test.txt')
print(result)
return label, prob, result
补充:用gensim.model.fasttext获取词向量
from gensim.models.fasttext import FastText
FASTEXT_SIZE = 100
def get_fasttext_voc(data, word_index):
'''
利用fasttext获取词向量
'''
fasttext_model = FastText([data],
size=FASTEXT_SIZE, # 需要学习的嵌入大小(默认为100)
window=3, # 上下文窗口大小(默认5)
min_count=1, # 忽略出现次数低于此值的单词(默认值5)
iter=10, # epoch(默认5)
min_n = 3, # char ngram的最小长度(默认值3)
max_n = 6, # char ngram的最大长度(默认值6)
word_ngrams = 0) # 如果为1,使用子单词(n-grams)信息丰富单词向量。如果是0,这就相当于Word2Vec
# 获取词向量词典
word_voc_dict = fasttext_model.wv.vocab
word_voc_list = fasttext_model.wv.index2word
# 获取词向量列表
wordEmbedding = np.zeros((len(word_index) + 1, FASTEXT_SIZE))
for word, i in word_index.items():
if word in fasttext_model:
wordEmbedding[i] = fasttext_model[word]
return word_voc_dict, word_voc_list, wordEmbedding
完整代码+数据集见github