文章目录
0x00 前言
最近开始学习深度学习,所以打算以写博客的形式来记录自己的学习过程。
本篇博客的目的是梳理熟悉深度学习的整体框架、神经网络模型及代码的构建以及加深深度学习整个过程的理解。基于目的,选择了较为简单的电影评论分类问题为例子。参考的书籍是 [美]Francois Chollet
著的《Python深度学习》,结合书本内容的同时也会提出我自己对部分细节的理解和总结。
运行环境:
Name | Version |
---|---|
python | 3.6 |
keras | 2.2.4 |
tensorflow-gpu | 1.14.0 |
Numpy | 1.16.2 |
cuda | 10.1 |
cudnn | 7.6.5 |
0x01 整体概述
【问题描述】:电影评论二分类想实现的工作其实就是将评论分为两个阵营——正面和负面。
【实现过程】:评论数据输入训练好的神经网络模型,得出一个预测值(此处预测值为正面或者负面),期待该预测值就为真实目标值或者为真的概率尽可能高。那么重点就是如何训练神经网络模型,据流程图,模型由若干个层组成,而每一层都对输入的数据做某种变换,最后得出一个预测值(呵呵很神奇),当然一开始这个预测值很可能不准确,那么到底有多不准确,由损失值来衡量,损失值可以理解为预测值到真实值之间的距离(通常距离越小越好),而损失函数就是计算这个距离用的。我们要训练的对象具体来说就是各层中的权重参数w,权重参与数据变换的过程,所以理论上取一组合适的权重,可以使得预测值最准确。那么如何得到合适的权重就是优化器的工作,优化器会依据损失值来更新权重。总结一下就是以下几个步骤:
(1)将数据输入神经网络模型,输出预测值
(2)损失函数依据真实目标值计算损失值
(3)优化器依据损失值更新权重
于是不断地输入数据,反复迭代,重复以上步骤就会得到一组较为合适的权重。当然会存在过拟合现象,先按下不表。
至此,就是模型训练的大致流程。不过依旧存在很多疑问,例如全连接层是干什么的?二元交叉熵是什么?优化器具体是怎么工作的?等等。下面展开来讲。
0x02 细节展开
2.1 数据预处理
前面的整体概述中没有提到数据处理的有关内容,在实际应用中,数据处理是一块重要的部分。因为输入模型的数据需要符合统一格式和特定的数据结构,而不做处理的原始数据往往不符合模型输入的条件,所以要进行数据预处理。
2.1.1 获取数据
本例数据直接来自IMDB数据库
from keras.datasets import imdb
# 加载数据
# 所加载的数据集其实是二维列表
# 每条评论是由其单词的整数索引构成的向量
(train_data, train_lables), (test_data, test_lables) = imdb.load_data(num_words=10000) # 10000意思是只保留最常出现的前10000单词
书本引用:
参数num_words=10000的意思是仅保留训练数据中前10000个最常出现的单词。低频单词将被舍弃。这样得到的向量数据不会太大,便于处理。
trian_data、test_data: 由评论组成的二维列表(samples,features),第一维是样本批量维度,第二维是评论。每一条评论都是由其单词的整数索引构成的一维向量(一个单词对应一个整数,这个整数不会超过9999)
train_lables、test_lables: 0、1组成的一维列表,0代表负面评论,1代表正面评论。数据标签,所谓的真实目标值,与每条评论呈一一对应关系。
2.1.2 处理数据
import numpy as np
# 准备数据(合适的格式)
# 因为神经网络需要传入张量的数据结构,所以要将数据向量化,将所有评论都向量化到一样的维度此处化到10000维,因为根据最常用的前10000的单词,单词所对应的序号最大为9999
# 对列表进行one-hot编码,将其转化为0和1组成的向量
def vectorize_sequences(sequences, dimension=10000): # 这里的 sequence 其实是传入的(训练集/测试集)
results = np.zeros((len(sequences), dimension)) # 创建零矩阵(二维张量)
for i, sequence in enumerate(sequences):
# 这里其实有点桶排的味道,将下标索引为该值的元素置1,表示有该值,其余为0
results[i, sequence] = 1. # [1.]表示此处数据类型为浮点
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
# 将标签也向量化,因为标签本来就是一维的向量了,所以直接转化成张量的数据结构就可了
y_train = np.asarray(train_lables).astype('float32')
y_test = np.asarray(test_lables).astype('float32')
代码解释请参考注释☝
此处之所以将数据类型转换为浮点型,给出我自己的理解:拓展学习空间。
将数据集类型转化为浮点型以后,在训练过程中的权重、损失值等也都从整数向浮点转变。而权重就是神经网络学习到的东西,转为浮点以后,它能够表示的范围就变大了,也就拓展了学习空间。这里的权重其实是张量矩阵,下文再展开来讲。
2.2 神经网络模型搭建
在讲解以下内容的时候,可能会有这样的疑问:为什么要这样那样搭建模型就可以了?
事实上根据不同的实际问题,要搭建不同的神经网络,而为什么要这样搭建,更多的是经验之谈。虽然存在一些原则性的指导,可以指导你采取某种模型架构更加合适,但是很难严谨科学逻辑地推出具体哪种模型是最好的。所以深度学习更偏向是一门工程性科学,需要不断实践。
本篇博客的目的也不是帮助你建立搭建模型的直觉,所以你要知道,接下来针对电影评论分类问题所搭建的模型,是已经已知且比较合适的。当然也可以采取其他不同的尝试。
2.2.1 模型架构
这里采用Sequential类来做层的线性堆叠架构,这是目前最常见的网络架构。所谓线性堆叠,从前文的流程图可以看出,是由单一输入映射到单一输出,再依次顺序连接各层。“深度学习”的深度也就体现在层的堆叠上。
from keras import models
model = models.Sequential()
这样子我们建立了一个模型的框架,之后就可以往这个框架中添加层。当然,除了线性堆叠的架构,还有其他各种各样的有向无环的拓扑架构可以采用。
2.2.2 层
对于电影评论二分类问题,此处使用全连接层(fully connected layer,也叫密集层 dense layer),激活函数采用relu、sigmoid。
from keras import layers
# Dense的第一个参数是该层的输出维度,也可以认为是该层的学习空间
model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) # 这里的10000指的是每个输入样本数据的维度,不包含样本数量的维度
model.add(layers.Dense(16, activation='relu')) # 从第二层开始,就不用写输入样本数据的维度,因为自动根据上一层进行调节
model.add(layers.Dense(1, activation='sigmoid'))
代码解释请参考注释☝
全连接层
不同的张量格式与不同的数据处理类型需要用到不同的层。每条电影评论是简单的向量数据,保存在形状为(samples, features)的2D张量中,适合采用全连接层 [对应于keras的Dense类]。
【全连接层做了什么工作?】
每个全连接层都做了以下张量矩阵的线性运算:
o u t p u t = d o t ( i n p u t , w )