【学习笔记】一些基础的神经网络

下文不妨假定神经网络的任务都是给数据分类,回归/预测还不会。

参数:机器学习的内容

超参数:人手动设置的数值,比如学习率、训练轮数

多层感知器 MLP

在 input layer 和 output layer 之间有一堆 hidden layer,每两层之间可以理解成一张完全二分图,二分图的邻接矩阵上有一些权重,随机初始化。

将图片的每个像素点抽出来变成向量之后在二分图上矩阵乘法得到第一层的结果,对这个结果向量每个数变换一下,常用的变换(激活函数)有 ReLU:和 0 取 \(\max\) / Sigmoid:\(\dfrac{1}{1+e^{-x}}\)/ \(\tanh\)\(\dfrac{e^x-e^{-x}}{e^x+e^{-x}}\)

最后得到一个长度为 output layer 大小的一维向量。比如在 MNIST 数据集里面就是 \([10,1]\) 大小的向量。向量里面每个数字再做一个 softmax 变换使得其总和为 1,于是第 \(i\) 的数字就表示它是 \(i\) 的概率。

训练集里面每个数字都标注了分类,或者说黑盒应该输出的向量是一个大小为 \([10,1]\) 的向量,只有一个数字为 \(1\)。那么根据差值进行反向传播。

反向传播主要是考虑链式法则。最开始的损失函数是 |output_layer| 元函数,记作 \(F(x_1,\dots x_m)\)\(\dfrac{d F}{d x_i}\) 可以根据定义计算。

记倒数第二层的数据为 \(y_1\dots y_n\) 那么根据链式法则 \(\dfrac{d F}{dy_j}=\dfrac{dF}{dx_i}\dfrac{dx_i}{dy_j}\)

根据矩阵乘法的定义,\(x_i\) 可以看成关于 \(y_1\dots y_n\)\(n\) 元函数(\(x_i\sum\limits_{j=1}^n y_jw_{j,i}\)

有激活函数的情况就是 \(\dfrac{dF}{dy_i}=\dfrac{dF}{dy'_ {i}} \dfrac{d y'_ {i}}{d y_i}\) 由于 \(y'_ i=f(y_i)\) 中的 \(f\) 已知,那么导数也就行了。

你如果理解到这是一个有向图上跑链式法则就可以了

此时让 \(w_{i,j}\) 加上 \(w_{i,j}\times\) learning rate \(\times \dfrac{d F(x_j)}{d y_i}\) 就行了

一些可能存在的问题:

  • 过拟合:神经网络过于复杂,直接把图片信息背过了,通过矩阵乘法维护了出来。

  • 梯度消失:因为在反向传播过程中,参数是要损失函数变化最大的方向逆过来更改,找不到梯度(梯度几乎为 0 )那么就是参数消失。\(\tanh\) 函数和 \(\text{ReLU}\) 函数可以在一定程度上缓解 \(\text{Sigmoid}\) 函数造成的问题

  • 梯度爆炸:梯度太大,变量更新程度太大,导致网络参数训练的时候没法收敛;解决方法就是 归一化,梯度裁剪(设置阈值使其保持在一定范围内)

卷积神经网络 CNN

MLP 中每层都是一个大矩阵,参数过多,不容易训练。同时他把图片拆成了一维向量,丧失了图片的二维特性。

CNN 每层设置一些卷积核(大小形状任意),这些卷积核和之前的图片进行差卷积:\(w'_ {i,j}=\sum\limits_{x,y}w_{i+x,j+y}\times c_{x,y}\)(边界部分直接把矩阵展开,多出来的部分置 \(0\))。

以识别图片为例,彩色二维图像是 RGB 叠加的,灰度图像是黑白单维度的,那么称 RGB 图像有三个 channel(通道),灰度图像有 1 个channel。记 CNN 某层 \(m\) 卷积核。我们让新 channel 的数量为 \(m\),原先的 3 个 channel 和这 \(m\) 个卷积核卷积之后的结果按照一定权重叠加(权重要训练)得到新的 \(m\) 个二维图片。

在 output layer 前再用一个全连接层映射到一维张量即可

一种可视化黑盒的方式(我现在唯一会的):把 hidden layer 中所有 channel 的中间值加起来再变成一个灰度图像,就完成了可视化。

反向传播过程和 MLP 一样,你发现每层只有九个参数。根据公式,导数应为 \(n\times m\) 个位置求出来的和。或者说还是有向图推一下。

CNN 网络结构中除了 卷积层、全连接层 之外还有 池化层,它可以实现将二位图片缩小,比如将 \(4\times 4\) 的二维矩阵上下左右四个 \(2\times 2\) 的矩阵中的每个 通过某种加权平均方式缩成一个格子,得到新 \(2\times 2\) 的矩阵。它可以 “减少像素数目,减小模型复杂性”

循环神经网络 RNN

RNN 的基本结构包括一个循环单元(或称为RNN单元)和一个隐藏状态(hidden state)。RNN 单元接受当前时间步的输入以及前一个时间步的隐藏状态,并输出当前时间步的隐藏状态。这个隐藏状态可以被视为网络的记忆或状态,能够捕获之前输入的信息。

假设我们有一个序列数据,长度为 \(T\),第 \(t\) 个时间步的输入为 \(x(t)\),隐藏状态为 \(h(t)\),则在 RNN 中,每个时间步的运算可以表达为:\(h(t) = f(W * x(t) + U * h(t-1) + b)\)

其中,f是激活函数(通常为 \(\tanh\) 或 ReLU),\(W\) 是当前时间步输入的权重矩阵,U是前一个时间步隐藏状态的权重矩阵,\(b\) 是偏置向量。

前向传播把最后一层的隐藏状态连一个完全层到 output layer,反向传播仍然是利用有向图来递推。

长短期记忆网络 LSTM

1.决定上一时间的隐藏状态中有哪些需要遗忘
2.决定当前输入层里面有哪些需要记忆
3.计算新的隐藏层

全都不用显式实现,kersa 库里面啥都有,看看咱 chatgpt 给的实现(预测 \(\sin\) 函数值):

# 创建LSTM模型
def create_lstm_model(n_steps, n_features):
    model = Sequential()
    model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features))) 
	# LSTM(遗忘率,激活函数,数据形状)
    model.add(Dense(1)) # dense 是全连接层,本问题显然满足大小为 1
    model.compile(optimizer='adam', loss='mse')
    return model

# 创建并训练LSTM模型
model = create_lstm_model(n_steps, n_features) # 第一个参数是时间步,第二个是特征数量
model.fit(data, label, epochs=200, verbose=1)


	# verbose=0:表示不输出训练过程中的任何信息。
	# verbose=1:表示输出进度条,例如在每个训练周期(epoch)完成时显示一个进度条或类似的指示器,指示训练的进展情况。
	# verbose=2:表示每个训练周期完成时输出一行信息,显示训练过程中的一些基本指标,例如损失函数值和准确率等。

这个问题中 n_feature = 1 ,因为一元回归问题中只有一个参数。如果是房子,你要考虑 面积、位置、楼间距,参数就多了。

门控循环单元 GRU

可以在一定程度上解决 RNN 的梯度消失梯度爆炸问题。

GRU通过引入一种门控机制来解决这个问题,包含了两个重要的门控单元:更新门(Update Gate)和重置门(Reset Gate)。这些门控单元可以选择性地更新和重置神经网络中的状态。

ChatGPT 写的代码没看懂。

无监督学习 VS 有监督学习

https://zhuanlan.zhihu.com/p/33919739

无监督学习常常被用于数据预处理。一般而言,这意味着以某种平均-保留的方式压缩数据,比如主成分分析(PCA)或奇异值分解(SVD),之后,这些数据可被用于深度神经网络或其它监督式学习算法。

二分类问题

二分类问题,即判断 是/否 问题中,有如下几个概念

  • True Positive (TP):实际为正例,并被正确预测为正例的样本数量。

  • False Positive (FP):实际为负例,但被错误地预测为正例的负例样本数量。

  • True Negative (TN):实际为负例,并被正确预测为负例的样本数量。

  • False Negative (FN):实际为正例,但被错误地预测为负例的正例样本数量。

由此导出了一些概念

  • 精确率:精确率(Precision)是在分类问题中用于衡量模型预测的准确性的指标之一。它表示在所有被模型预测为正例的样本中,实际上真正为正例的比例。计算公式是 \(\dfrac{\text{TP}}{\text{TP+FP}}\)

  • 召回率:召回率(Recall),也称为灵敏度(Sensitivity)或真正例率(True Positive Rate,TPR),是在二分类问题中用于衡量模型对正例样本预测能力的指标。它表示在所有实际为正例的样本中,模型能够正确预测为正例的比例。计算公式是 \(\dfrac{\text{TP}}{\text{TP+FN}}\)

  • F1-score = 2 * (Precision * Recall) / (Precision + Recall),越接近1表示模型的性能越好

多分类问题中的召回率/精确率是每个类的召回率/精确率的平均,每类的概率计算也是依照上面的定义

代码实现

参数读取与保存

【torch version】


# 读取
model_parameters_file = 'document_name.pth'
# 检查文件是否存在
if os.path.exists(model_parameters_file):
	# 如果文件存在,加载参数
	model.load_state_dict(torch.load(model_parameters_file))
	print("成功加载模型参数!")
else:
	# 如果文件不存在,输出错误信息或者重新训练模型
	print("找不到模型参数文件。请确保文件存在或者重新训练模型。")


# 保存
torch.save(model.state_dict(),'document_name.pth')

这个 if/else 是为了避免第一次运行的时候没有数据可以读取的窘境报错。

【tensorflow version】

Save

import tensorflow as tf

# 假设你有两个变量 var1 和 var2
var1 = tf.Variable([[1.0, 2.0], [3.0, 4.0]], dtype=tf.float32)
var2 = tf.Variable([[5.0, 6.0], [7.0, 8.0]], dtype=tf.float32)

# 定义一个 Checkpoint 对象,并设置需要保存的变量
checkpoint = tf.train.Checkpoint(var1=var1, var2=var2)

# 将变量保存到文件
checkpoint.save('/path/to/save/dir')

Load

import tensorflow as tf

# 创建与保存时相同的模型结构
var1 = tf.Variable([[0.0, 0.0], [0.0, 0.0]], dtype=tf.float32)
var2 = tf.Variable([[0.0, 0.0], [0.0, 0.0]], dtype=tf.float32)

# 定义一个 Checkpoint 对象,并设置需要保存的变量
checkpoint = tf.train.Checkpoint(var1=var1, var2=var2)

# 从保存的文件中恢复变量
checkpoint.restore('/path/to/save/dir')

# 现在,var1 和 var2 的值已经恢复为之前保存的值
print(var1)
print(var2)

待学习的事项

Xavier/ Glorot 初始化

posted @ 2023-07-30 09:41  没学完四大礼包不改名  阅读(141)  评论(1)    收藏  举报