2021SC@SDUSC
附所有代码链接:https://github.com/Jayshen0/ Unsupervised-Deep-Keyphrase-Generatio
本篇相关代码模块为:model.py(一)
在之前的几篇博客中,我们分析了extract.py代码文件,一起了解了前期的数据集准备工作,生成用于训练和测试模型的存在、缺失关键词语库。接下来我们正式开始分析学习用于深度关键短语生成的模型的model.py文件。
可以看到,model.py文件共包含三个类,分别为:Encoder类编码,Decoder类解码,Seq2Seq模型生成序列。本篇论文编码器使用 BiLSTM实现,解码器使用 LSTM实现,以Encoder-Decoder框架为基础实现了Seq2Seq模型。
首先简要介绍一下这些模型:
- Seq2Seq 是自然语言处理中的一种重要模型,包括编码器 (Encoder) 和解码器 (Decoder) 两部分,可以用于机器翻译、对话系统、自动文摘。
- LSTM的全称是Long Short-Term Memory,它是RNN(循环神经网络)的一种。LSTM由于其设计的特点,非常适合用于对时序数据的建模,如文本数据。
- BiLSTM是Bi-directional Long Short-Term Memory的缩写,是由前向LSTM与后向LSTM组合而成。两者在自然语言处理任务中都常被用来建模上下文信息。
因此,我们又正式开启了一个新的领域:神经网络模型在nlp领域的应用学习。作为初学者,我还是打算从基础开始,简要了解神经网络的基本思想之后,再对这几个模型进行深入分析,这样分析起源码才会更加游刃有余,一路畅快。
所以,接下来几篇博客都是对本篇代码涉及到的神经网络模型基础的相关代码的分析和学习,在有了一定的基础后,再开始研究model.py代码。
一.神经网络基础
1.线性回归
将一个实例的一系列特征值用列向量x表示,线性回归就是求出一组参数w,作为这些属性的权重,使得预测输出可以表示为的线性组合,这个值是连续值,所以可以用来处理回归问题。
可以类比以前经常说的求每个方程中未知量前系数的问题。又可以根据自变量x个数的不同,将线性回归分为单变量线性回归和多元线性回归。
2.logistic回归、多项式回归
logistic回归是一种广义线性回归(generalized linear model),因此与多元线性回归分析有很多相同之处。它们的模型形式基本上相同,都具有 w‘x+b,其中w和b是待求参数,其区别在于他们的因变量不同,多元线性回归直接将w‘x+b作为因变量,即y =w‘x+b,而logistic回归则通过函数L将w‘x+b对应一个隐状态p,p =L(w‘x+b),然后根据p与1-p的大小决定因变量的值。如果L是logistic(也叫sigmoid)函数,就是logistic回归,如果L是多项式函数就是多项式回归。
比如,把上面的通过 sigmoid函数映射到(0,1)上,并划分一个阈值,大于阈值的分为一类,小于等于分为另一类,可以用来处理二分类问题。 更进一步:对于N分类问题,则是先得到N组w值不同的 w’x+b,然后归一化,比如用 softmax函数,最后变成N个类上的概率,可以处理多分类问题。
3.非线性回归模型
线性回归和非线性回归模型之间的区别见zlibo丶的博客-CSDN博客_线性回归和非线性回归的区别
简单来说,当模型中的所有项都是以下之一时,回归模型是线性的:
- 常数
- 某参数乘以一个独立变量
即形为是线性回归模型,其余的都是非线性回归。
4.神经网络
神经网络做的事就像是逻辑回归,但它不是使用原本的x1、x2、x3作为特征。而是用a1、a2、a3(通过学习得到的函数值)作为新的特征。就像多个神经元组合在一起,形成了神经网络。我们来看以下具有神经网络思维的Logistic回归的代码示例,更好地理解这一思想。
1.首先加载数据
train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = load_dataset()
m_train = train_set_y.shape[1] #训练集里图片的数量。
m_test = test_set_y.shape[1] #测试集里图片的数量。
num_px = train_set_x_orig.shape[1] #训练、测试集里面的图片的宽度和高度(均为64x64)。
2.把train_set_x_orig(m_train,num_px,num_px,3)的数组,转换为(num_px*num_px*3,m_train),test同理。
即:维度为(64,64,3)的numpy数组重新构造为(64 x 64 x 3,1)的数组。每张图片64*64像素,每个像素点由RGB三原色构成,所以乘3.
#方法:X_flatten = X.reshape(X.shape[0],-1).T
要把原numpy数组变成每列代表一个平坦的图像,取转置后,使得数组有X.shape[0]=m_train列,-1表示让程序计算一共有多少行
#将训练集的维度降低并转置。
train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
#将测试集的维度降低并转置。
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
print("---------------------------------------------------")
print ("训练集降维后的维度: " + str(train_set_x_flatten.shape))
print ("训练集_标签的维数 : " + str(train_set_y.shape))
print ("测试集降维后的维度: " + str(test_set_x_flatten.shape))
print ("测试集_标签的维数 : " + str(test_set_y.shape))
#标准化数据集,使值在[0,1]之间。因为像素值原范围是0-255
train_set_x = train_set_x_flatten / 255
test_set_x = test_set_x_flatten / 255
注:我理解的降维:原来用三维数据表示的特征,现在用一维就能表示。即:放在了单层网络上即可。但不代表数据量变小。
测试结果:
3.初始化参数w和b。为w创建一个维度为(dim,1)的0向量,并将b初始化为0。
参数: dim - 我们想要的w矢量的大小(或者这种情况下的参数数量)
返回: w - 维度为(dim,1)的初始化向量。
b - 初始化的标量(对应于偏差)
def initialize_with_zeros(dim):
w = np.zeros(shape = (dim,1))
b = 0
#使用断言来确保我要的数据是正确的
assert(w.shape == (dim, 1)) #w的维度是(dim,1)
assert(isinstance(b, float) or isinstance(b, int)) #b的类型是float或者是int
return (w , b)
4.实现前向和后向传播的成本函数及其梯度。
参数:
- w - 权重,大小不等的数组(num_px * num_px * 3,1)
- b - 偏差,一个标量
- X - 矩阵类型为(num_px * num_px * 3,训练数量)
- Y - 真正的“标签”矢量(如果非猫则为0,如果是猫则为1),矩阵维度为(1,训练数据数量)
返回:
- cost- 逻辑回归的负对数似然成本
- dw - 相对于w的损失梯度,因此与w相同的形状
- db - 相对于b的损失梯度,因此与b的形状相同
def propagate(w, b, X, Y):
m = X.shape[1]#训练数量
#正向传播
A = sigmoid(np.dot(w.T,X) + b) #计算激活值,请参考公式2。
cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A))) #计算成本,请参考公式3和4。
#反向传播
dw = (1 / m) * np.dot(X, (A - Y).T) #请参考视频中的偏导公式。
db = (1 / m) * np.sum(A - Y) #请参考视频中的偏导公式。
#使用断言确保我的数据是正确的
assert(dw.shape == w.shape)
assert(db.dtype == float)
cost = np.squeeze(cost)#使用np.squeeze的目的是压缩维度,只有压缩后的值才能进行解码操作
assert(cost.shape == ())
#创建一个字典,把dw和db保存起来。
grads = {
"dw": dw,
"db": db
}
return (grads , cost)
我们来测试一下:
#初始化一些参数
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
grads, cost = propagate(w, b, X, Y)
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))
print ("cost = " + str(cost))
测试结果:
5.此函数通过运行梯度下降算法来优化w和b
参数:
- w - 权重,大小不等的数组(num_px * num_px * 3,1)
- b - 偏差,一个标量
- X - 维度为(num_px * num_px * 3,训练数据的数量)的数组。
- Y - 真正的“标签”矢量(如果非则为0,如果是则为1),矩阵维度为(1,训练数据的数量)
- num_iterations - 优化循环的迭代次数
- learning_rate - 梯度下降更新规则的学习率
- print_cost - 每100步打印一次损失值
返回:
- params - 包含权重w和偏差b的字典
- grads - 包含权重和偏差相对于成本函数的梯度的字典
- 成本 - 优化期间计算的所有成本列表,将用于绘制学习曲线。
def optimize(w , b , X , Y , num_iterations , learning_rate , print_cost = False):
costs = []
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
w = w - learning_rate * dw
b = b - learning_rate * db
#记录成本
if i % 100 == 0:
costs.append(cost)
#打印成本数据
if (print_cost) and (i % 100 == 0):
print("迭代的次数: %i , 误差值: %f" % (i,cost))
params = { "w" : w,
"b" : b }
grads = {"dw": dw,
"db": db }
return (params , grads , costs)
提示:我们需要写下两个步骤并遍历它们:
1)计算当前参数的成本和梯度,使用propagate()。
2)使用w和b的梯度下降法则更新参数。
6.通过调用之前实现的函数来构建逻辑回归模型
参数:
X_train -numpy的数组,维度为(num_px * num_px * 3,m_train)的训练集
Y_train -numpy的数组,维度为(1,m_train)(矢量)的训练标签集
X_test -numpy的数组,维度为(num_px * num_px * 3,m_test)的测试集
Y_test -numpy的数组,维度为(1,m_test)的(向量)的测试标签集
num_iterations - 表示用于优化参数的迭代次数的超参数
learning_rate - 表示optimize()更新规则中使用的学习速率的超参数
print_cost - 设置为true以每100次迭代打印成本
返回:
d - 包含有关模型信息的字典。
def model(X_train , Y_train , X_test , Y_test , num_iterations = 2000 , learning_rate = 0.5 , print_cost = False):
w , b = initialize_with_zeros(X_train.shape[0])
parameters , grads , costs = optimize(w , b , X_train , Y_train,num_iterations , learning_rate , print_cost)
#从字典“参数”中检索参数w和b
w , b = parameters["w"] , parameters["b"]
#预测测试/训练集的例子
Y_prediction_test = predict(w , b, X_test)
Y_prediction_train = predict(w , b, X_train)
#打印训练后的准确性
print("训练集准确性:" , format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100) ,"%")
print("测试集准确性:" , format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100) ,"%")
d = {
"costs" : costs,
"Y_prediction_test" : Y_prediction_test,
"Y_prediciton_train" : Y_prediction_train,
"w" : w,
"b" : b,
"learning_rate" : learning_rate,
"num_iterations" : num_iterations }
return d
d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
运行结果:
至此,我们实现了对一个神经网络的初步运用。通过搭建一个简单的神经网络,深入了解了它的思想与实现细节,为我们后面循环神经网络的进一步学习打下了基础。