前言
文章概述《深度学习详解》- 1.2
书中介绍了深度学习中的梯度下降优化算法及其在构建线性模型和神经网络中的应用。首先,通过梯度下降法寻找使损失函数最小化的模型参数,该过程包括计算梯度、更新参数及重复此过程直至达到最优解。其次,文中探讨了使用分段线性和Sigmoid函数构建模型的能力,强调了模型复杂度与参数数量的关系。此外,通过引入ReLU激活函数和增加网络层数,进一步提高了模型的拟合能力和预测准确性。最后,讨论了深度学习模型的训练过程,包括反向传播算法的应用,并强调了在实践中选择合适模型的重要性,以防过拟合现象的发生。整体而言,文章深入浅出地阐释了深度学习的基本原理和实践技巧,为进一步学习和研究深度学习打下了坚实的基础。
个人学习笔记以及概念梳理,望对大家有所帮助。
思维导图1.2
常见的激活函数(补充)
激活函数 | 定义 | 优点 |
ReLU (Rectified Linear Unit) | ![]() | 简单快速计算,能够有效缓解梯度消失问题 |
Sigmoid | ![]() | 输出范围在 (0, 1) 之间,适合于概率估计 |
tanh (Hyperbolic Tangent) | ![]() | 输出范围在 (-1, 1) 之间,中心化有助于学习 |
Leaky ReLU | ![]() | 解决了ReLU的“死区”问题,允许负数区域的小梯度流过 其中 α 是一个小正数 |
Softplus | ![]() | 输出平滑且可微,通常用于构建其他函数 |
Swish | ![]() | 自适应权重调整,性能通常优于ReLU,尤其在深度网络中 其中 σ(x)是Sigmoid函数 |
大致的图形:
涉及的一些术语
术语 | 解释 |
过拟合(Overfitting) | 模型在训练数据上表现得很好,但在未见过的数据(如测试数据)上表现较差的现象。通常是由于模型过于复杂或训练数据不足造成的。 |
欠拟合(Underfitting) | 模型既不能很好地拟合训练数据,也不能很好地泛化到新数据。表现为无论是训练数据还是测试数据,模型的表现都很差。 |
神经元(Neuron) | 神经网络的基本单元,可以被视为一个小型的计算模块。神经元接收输入,通过激活函数处理后产生输出。 |
神经网络(Neural Network) | 由多个神经元连接而成的网络,用于模拟人脑处理信息的方式。神经网络可以用于各种机器学习任务,如分类、回归等。 |
隐藏层(Hidden Layer) | 神经网络中位于输入层和输出层之间的层。隐藏层使得神经网络可以学习更加复杂的特征表示。 |
深度学习(Deep Learning) | 一种利用多层神经网络进行学习的技术。深度学习模型通常包含多个隐藏层,可以学习到输入数据的高层次抽象表示。 |
批量(Batch) | 训练神经网络时使用的数据子集。在每次更新模型参数时,会使用一个批量的数据来计算梯度。 |
回合(Epoch) | 在训练过程中,遍历完整训练集一次称为一个回合。每个回合中可能会更新参数多次,取决于批量的大小。 |
学习过程遇到的一些问题的理解:
1神经网络的大致结构
补充说明
输入层(Input Layer):
输入层负责接收原始数据,通常对应于输入特征的数量。
输入层的每个神经元对应一个输入特征。
隐藏层(Hidden Layers):
隐藏层位于输入层和输出层之间,负责进行数据的处理和转换。
每个隐藏层通常由多个神经元组成,每个神经元执行加权求和操作并经过激活函数。
隐藏层可以有多层,形成深度神经网络。
输出层(Output Layer):
输出层负责产生最终的预测结果。
输出层的神经元数量取决于问题的类型,例如对于分类问题,输出层神经元数量通常等于类别数;对于回归问题,输出层只有一个神经元。
连接权重(Weights):
权重连接输入层、隐藏层以及输出层中的神经元。
权重决定了输入特征对输出的影响程度。
权重的值通过训练过程中的优化算法(如梯度下降)不断调整以最小化损失函数。
偏置(Biases):
偏置为每个神经元提供额外的可调参数,帮助网络学习更复杂的函数。
偏置的值也是通过训练过程中的优化算法进行调整的。
激活函数(Activation Functions):
激活函数用于引入非线性,使得网络能够学习非线性映射。
激活函数作用于每个神经元的输出,常见的激活函数包括ReLU、Sigmoid和tanh等。
2神经网络的工作流程
前向传播(Forward Propagation):
输入数据通过网络向前传播,经过一系列的权重乘法和激活函数。
每个隐藏层计算加权和并经过激活函数产生输出。
最终输出层产生预测结果。
损失计算(Loss Calculation):
使用损失函数来衡量预测结果与实际结果之间的差距。
反向传播(Backpropagation):
根据损失函数计算梯度,并反向传播通过网络以更新权重和偏置。
更新的目的是最小化损失函数。
优化算法(Optimization Algorithm):
如梯度下降等算法用于调整权重和偏置以减小损失。
代码运行
运用python 对波士顿房价数据集进行处理
验证不同的激活函数组合是否会影响整体的MSE
代码运行前申明:在进行神经网络的处理时,其运行结果可能会发生改变,属于正常现象。
简单解释一下,就不做展开:神经网络训练结果的变化是由于多种因素共同作用的结果(如随机初始化、随机种子、硬件和环境差异、非线性激活函数等)
以三层神经网络为例,取其中常见的三个激活函数:ReLU() Sigmoid() Tanh()
三个激活函数对应的序号
# Define a mapping from activation function names to numbers
activation_numbers = {
'relu': 1,
'sigmoid': 2,
'tanh': 3
}
进行如下组合
combinations = [
('relu', 'relu', 'relu'), # 3
('tanh', 'tanh', 'tanh'),
('sigmoid', 'sigmoid', 'sigmoid'),
('relu', 'relu', 'tanh'), # 2
('relu', 'tanh', 'relu'),
('tanh', 'relu', 'relu'),
('relu', 'relu', 'sigmoid'),
('relu', 'sigmoid', 'relu'),
('sigmoid', 'relu', 'relu'),
('tanh', 'tanh', 'relu'),
('tanh', 'relu', 'tanh'),
('relu', 'tanh', 'tanh'),
('tanh', 'tanh', 'sigmoid'),
('tanh', 'sigmoid', 'tanh'),
('sigmoid', 'tanh', 'tanh'),
('sigmoid', 'sigmoid', 'tanh'),
('sigmoid', 'tanh', 'sigmoid'),
('tanh', 'sigmoid', 'sigmoid'),
('sigmoid', 'sigmoid', 'relu'),
('sigmoid', 'relu', 'sigmoid'),
('relu', 'sigmoid', 'sigmoid'),
('relu', 'sigmoid', 'tanh'), # 1
('relu', 'tanh', 'sigmoid'),
('sigmoid', 'relu', 'tanh'),
('sigmoid', 'tanh', 'relu'),
('tanh', 'relu', 'sigmoid'),
('tanh', 'sigmoid', 'relu')
]
首先,我们要了解,三层神经网络中不同的激活函数如果互换位置,确实可能会影响整体的均方误差(MSE)。这是因为激活函数的选择和位置会影响网络的学习能力和表达能力。
总代码如下:
import torch
import torch.nn as nn
import torch.optim as optim
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
# Load the dataset
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
label = raw_df.values[1::2, 2]
# Preprocess the data
scaler = StandardScaler()
data = scaler.fit_transform(data)
X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=0.2, random_state=42)
# Convert to PyTorch tensors
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)
# Define the model class
class Net(nn.Module):
def __init__(self, activation1, activation2, activation3):
super(Net, self).__init__()
self.fc1 = nn.Linear(13, 16) # Input layer to hidden layer
self.fc2 = nn.Linear(16, 8) # Hidden layer to hidden layer
self.fc3 = nn.Linear(8, 1) # Hidden layer to output layer
self.activation1 = activation1
self.activation2 = activation2
self.activation3 = activation3
def forward(self, x):
x = self.activation1(self.fc1(x))
x = self.activation2(self.fc2(x))
x = self.activation3(self.fc3(x))
return x
# Define the activation functions
activation_functions = {
'relu': nn.ReLU(),
'sigmoid': nn.Sigmoid(),
'tanh': nn.Tanh()
}
# Define a mapping from activation function names to numbers
activation_numbers = {
'relu': 1,
'sigmoid': 2,
'tanh': 3
}
# New combinations
combinations = [
('relu', 'relu', 'relu'), # 3
('tanh', 'tanh', 'tanh'),
('sigmoid', 'sigmoid', 'sigmoid'),
('relu', 'relu', 'tanh'), # 2
('relu', 'tanh', 'relu'),
('tanh', 'relu', 'relu'),
('relu', 'relu', 'sigmoid'),
('relu', 'sigmoid', 'relu'),
('sigmoid', 'relu', 'relu'),
('tanh', 'tanh', 'relu'),
('tanh', 'relu', 'tanh'),
('relu', 'tanh', 'tanh'),
('tanh', 'tanh', 'sigmoid'),
('tanh', 'sigmoid', 'tanh'),
('sigmoid', 'tanh', 'tanh'),
('sigmoid', 'sigmoid', 'tanh'),
('sigmoid', 'tanh', 'sigmoid'),
('tanh', 'sigmoid', 'sigmoid'),
('sigmoid', 'sigmoid', 'relu'),
('sigmoid', 'relu', 'sigmoid'),
('relu', 'sigmoid', 'sigmoid'),
('relu', 'sigmoid', 'tanh'), # 1
('relu', 'tanh', 'sigmoid'),
('sigmoid', 'relu', 'tanh'),
('sigmoid', 'tanh', 'relu'),
('tanh', 'relu', 'sigmoid'),
('tanh', 'sigmoid', 'relu')
]
results = []
best_combination = None
best_loss = float('inf')
for combo in combinations:
# Initialize the model with the current combination of activation functions
model = Net(activation_functions[combo[0]], activation_functions[combo[1]], activation_functions[combo[2]])
# Define loss function and optimizer
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# Train the model
num_epochs = 100
for epoch in range(num_epochs):
outputs = model(X_train)
loss = criterion(outputs, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Evaluate the model on the test set
with torch.no_grad():
predicted = model(X_test)
mse = mean_squared_error(y_test, predicted)
# Map the combo to a number for plotting
combo_number = int("".join(str(activation_numbers[act]) for act in combo))
results.append((combo_number, mse))
if mse < best_loss:
best_loss = mse
best_combination = combo
# Sort the results based on the MSE value
results.sort(key=lambda x: x[1])
# Extract the top 5 smallest and largest MSE values
top_5_smallest_mse = results[:5]
top_5_largest_mse = results[-5:]
# Combine them into a single list for plotting
combined_results = top_5_smallest_mse + top_5_largest_mse
# Extract the combo numbers and MSE values
combo_numbers, mses = zip(*combined_results)
# 设置每个柱形的宽度
width = 0.6 # 这个值可以根据需要调整
# 计算横坐标的中心位置
center_positions = np.arange(len(combo_numbers))
# 绘制柱状图
plt.figure(figsize=(10, 6))
# 绘制前五个柱状图(蓝色)
plt.bar(center_positions[:5], mses[:5], width=width, color='blue', tick_label=combo_numbers[:5])
# 绘制后五个柱状图(红色)
plt.bar(center_positions[5:], mses[5:], width=width, color='red', tick_label=combo_numbers[5:])
# 添加文本标签
for i in range(len(mses)):
plt.text(center_positions[i], mses[i], f'{mses[i]:.2f}', ha='center', va='bottom')
plt.xlabel('Activation Function Combinations (Numbers)')
plt.ylabel('Mean Squared Error (MSE)')
plt.title('Top 5 Smallest and Largest MSE for Activation Function Combinations')
plt.xticks(center_positions, combo_numbers, rotation=45) # 使用计算得到的中心位置作为横坐标的位置
plt.tight_layout() # 自动调整子图参数, 使之填充整个图像区域
plt.show()
print(f"Best combination of activation functions is {best_combination} with MSE: {best_loss:.4f}")
运行后得到如下结果:
注1:蓝色部分为其中mse前5个最好的组合,而红色为后5个最差的组合
注2:其中的111 代表着('relu', 'relu', 'relu')的组合,221则是('sigmoid', 'sigmoid, 'relu')
进一步处理,获得最好组合的神经网络,展示预测曲线vs真实数据曲线
代码如下:
# Plot the best model's predictions against the actual data
best_model = Net(activation_functions[best_combination[0]], activation_functions[best_combination[1]], activation_functions[best_combination[2]])
criterion = nn.MSELoss()
optimizer = optim.Adam(best_model.parameters(), lr=0.01)
# Train the best model again for the plot
num_epochs = 100
for epoch in range(num_epochs):
outputs = best_model(X_train)
loss = criterion(outputs, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Predict using the best model
with torch.no_grad():
predicted_best = best_model(X_test).numpy().flatten()
y_test_np = y_test.numpy().flatten()
# Plot the prediction curve vs actual data curve
plt.figure(figsize=(10, 6))
plt.plot(y_test_np, label='Actual Data', marker='o')
plt.plot(predicted_best, label=f'Predictions (MSE: {best_loss:.4f})', marker='x')
plt.legend()
plt.xlabel('Sample Index')
plt.ylabel('Value')
plt.title(f'Predictions vs Actual Data Using Best Activation Functions ({best_combination})')
plt.grid(True)
plt.show()
结果如下:
思考点:有兴趣的伙伴们,还可以进一步考虑其与不同回归预测的性能。
上一篇文章中略微有所涉及线性回归的预测曲线。可见 入门Task1:《深度学习详解》(Datawhale X 李宏毅苹果书 AI夏令营)-CSDN博客
小结
神经网络结构
- 输入层:接收原始数据。
- 隐藏层:进行数据处理和转换。
- 输出层:产生最终预测结果。
- 连接权重:决定输入特征对输出的影响程度。
- 偏置:为每个神经元提供额外的可调参数。
神经网络工作流程
- 前向传播:输入数据通过网络向前传递。
- 损失计算:衡量预测结果与实际结果之间的差距。
- 反向传播:计算梯度并更新权重和偏置。
- 优化算法:调整权重和偏置以减小损失。
波士顿房价预测实验
- 数据集:波士顿房价数据集。
- 方法:使用不同激活函数组合训练神经网络。
- 得出:最佳激活函数组合是 ReLU, ReLU, ReLU,具有最低的均方误差(MSE)。