前情提要:后端开发自学AI知识,内容比较浅显,偏实战;仅适用于入门了解,解决日常沟通障碍。
前馈神经网络是最基础的一类人工神经网络结构,也是深度学习的起点。它是由多层人工神经元按照顺序连接组成的模型,信息从输入层逐步向输出层传递(前向传播),没有循环或反馈。
1. FNN的基本结构
1.1 网络层组成
-
输入层:
- 接受原始数据作为输入。
- 例如:对图像数据来说,输入层是图像像素值。
-
隐藏层:
- 包含一个或多个神经元层,逐层提取特征。
- 神经元之间通过权重连接。
- 每个隐藏层使用激活函数为数据引入非线性。
-
输出层:
-
最后一层神经元,将计算结果输出。
-
输出的形式取决于任务:
- 分类任务:输出类别概率。
- 回归任务:输出连续值。
-
1.2 神经元的工作机制
2. FNN的特性
2.1 信息流动
- 数据从输入层开始,经过每个隐藏层,最后到达输出层。
- 没有循环或反馈,信息仅向前传播。
2.2 可扩展性
-
层数和每层神经元数量可以调整。
- 浅层网络:一两个隐藏层,适合简单任务。
- 深层网络:多层隐藏层,适合复杂任务。
2.3 模型优化
- 损失函数用于衡量预测结果与真实值之间的差异。
- 通过反向传播算法(Backpropagation)调整权重和偏置,最小化损失。
3. 训练过程
3.1 前向传播
- 数据从输入层开始,通过层间权重和激活函数逐步计算,到达输出层。
3.2 计算损失
-
使用损失函数衡量预测值与真实值的误差。
-
例如:
- 分类:交叉熵损失(Cross-Entropy Loss)。
- 回归:均方误差(Mean Squared Error)。
-
3.3 反向传播
-
通过梯度下降算法更新权重和偏置。
-
过程:
- 根据误差计算损失函数的梯度。
- 梯度反向传播到每一层。
- 按照梯度下降方向更新参数。
3.4 迭代优化
- 多次前向传播和反向传播,直到损失函数收敛或达到设定的训练轮次。
4. FNN的激活函数
激活函数是FNN的核心,用于引入非线性。常见激活函数包括:
-
Sigmoid:
- 输出范围:0,10, 10,1。
- 优点:适合概率输出。
- 缺点:梯度消失问题。
-
ReLU(Rectified Linear Unit) :
- 输出:max(0,x)\max(0, x)max(0,x)。
- 优点:计算简单,缓解梯度消失。
- 缺点:神经元可能"死亡"。
-
Tanh:
- 输出范围:−1,1-1, 1−1,1。
- 优点:比Sigmoid更适合归一化数据。
- 缺点:仍有梯度消失问题。
-
Softmax:
- 用于多分类任务,将输出转为概率分布。
应用场景
-
分类问题:
- 判断邮件是否为垃圾邮件。
- 预测信用卡交易是否欺诈。
-
回归问题:
- 房价预测。
- 销售额预测。
-
简单模式识别:
- 用户行为分析。
- 客户分层。
FNN 使用案例:预测学生考试成绩
1. 问题描述
我们用一个简单的前馈神经网络(FNN)来预测学生的考试成绩,基于以下特征:
- 学习时间
- 睡眠时间
- 学校支持情况
- 家庭环境
数据样式
school | sex | age | address | famsize | Pstatus | Medu | Fedu | traveltime | studytime | failures | schoolsup | famsup | paid | activities | nursery | higher | internet | romantic | famrel | freetime | goout | Dalc | Walc | health | absences | G1 | G2 | G3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
GP | F | 18 | U | GT3 | A | 4 | 4 | 2 | 2 | 0 | yes | no | no | no | yes | yes | yes | no | 4 | 3 | 4 | 1 | 1 | 3 | 6 | 5 | 6 | 6 |
MS | M | 17 | U | LE3 | T | 1 | 1 | 1 | 2 | 3 | no | yes | yes | no | no | yes | no | yes | 5 | 4 | 3 | 2 | 3 | 3 | 4 | 5 | 5 | 6 |
数据样式解读
- 每行记录一个学生的特征与成绩。
- 数据中既包含 数值型特征 (如年龄、成绩、学习时间),也包含 分类型特征(如学校、性别、家庭规模)。
- G1、G2、G3 是逐学期成绩,最终成绩 G1+G2+G3 通常作为模型预测目标。
2. 数据集
我们使用 UCI Machine Learning Repository 提供的 Student Performance Dataset:
- 数据集名称 :Student Performance Dataset
- 文件大小:约 30 KB。
- 特征数量:33 个。
- 目标:预测学生的最终成绩。
3. 案例实现
3.1 数据预处理
下载并加载数据集,进行以下操作:
- 去除无关列。
- 将分类变量(如学校类型)进行编码。
- 归一化连续变量(如学习时间)。
3.2 模型设计
- 输入层:接收 32 个输入特征。
- 隐藏层:两层,每层有 64 个神经元,激活函数为 ReLU。
- 输出层:1 个节点,预测最终成绩,激活函数为线性。
3.3 训练与验证
- 损失函数:均方误差(MSE)。
- 优化器:Adam。
- 数据划分:训练集 80%,验证集 20%。
4. 示例代码(使用 Python 和 Keras)
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error, mean_squared_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler
# 1. 加载数据并预处理
data = pd.read_csv("student-mat.csv", sep=";")
data['final_grade'] = data['G1'] + data['G2'] + data['G3']
data.drop(['G1', 'G2', 'G3'], axis=1, inplace=True)
# 编码分类特征
categorical_features = data.select_dtypes(include=['object']).columns
for col in categorical_features:
data[col] = LabelEncoder().fit_transform(data[col])
# 归一化数值特征
scaler = StandardScaler()
numeric_features = data.select_dtypes(include=['int64', 'float64']).columns
data[numeric_features] = scaler.fit_transform(data[numeric_features])
# 划分数据集
X = data.drop('final_grade', axis=1)
y = data['final_grade']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 2. 构建和训练模型
model = Sequential([
Dense(64, input_dim=X_train.shape[1], activation='relu'),
Dense(64, activation='relu'),
Dense(1) # 输出层
])
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=50, batch_size=32, verbose=0)
# 3. 可视化结果
# 3.1 训练与验证损失
plt.figure(figsize=(8, 5))
plt.plot(history.history['loss'], label='Training Loss', color='blue')
plt.plot(history.history['val_loss'], label='Validation Loss', color='orange')
plt.title('Model Loss During Training')
plt.xlabel('Epochs')
plt.ylabel('MSE Loss')
plt.legend()
plt.show()
plt.savefig('output1.png') # 保存为文件
# 3.2 真实值 vs. 预测值
y_pred = model.predict(X_test)
plt.figure(figsize=(8, 5))
plt.scatter(y_test, y_pred, alpha=0.6, color='green')
plt.title('True Grades vs Predicted Grades')
plt.xlabel('True Grades')
plt.ylabel('Predicted Grades')
plt.axline([0, 0], [1, 1], color='red', linestyle='--') # 理想对角线
plt.show()
plt.savefig('output2.png') # 保存为文件
# 3.3 误差分布
errors = y_test - y_pred.flatten()
plt.figure(figsize=(8, 5))
plt.hist(errors, bins=20, color='purple', alpha=0.7)
plt.title('Prediction Error Distribution')
plt.xlabel('Error')
plt.ylabel('Frequency')
plt.show()
plt.savefig('output3.png') # 保存为文件
# 4. 输出评估指标
mse = mean_squared_error(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
print(f"Mean Squared Error: {mse:.2f}")
print(f"Mean Absolute Error: {mae:.2f}")
5. 结果分析
图 1:训练与验证损失
- 内容:展示训练过程中模型损失(MSE)的变化。
- 解读:训练损失与验证损失逐渐下降,趋于收敛,验证损失更能反映模型在未知数据上的表现。
图 2:真实值 vs. 预测值
- 内容:散点图展示测试集中的真实成绩与预测成绩的对比。
- 解读:点越接近对角线(理想线),说明模型的预测越准确。
图 3:误差分布
- 内容:误差(预测值减真实值)的直方图。
- 解读:误差分布接近零且对称,说明模型表现良好。若误差偏离过多或呈明显非对称分布,则需优化模型。
评估指标
- MSE 反映误差的平方和,越小越好。
- MAE 是实际误差的平均值,更易解读。
模型性能
- 如果 MAE 小于 2,说明模型对成绩预测的偏差较小,可用于实际应用。
- 如需更高的精度,可考虑优化数据预处理、调整模型结构或进行超参数调优。
补充:通俗解释激活函数
可以简单理解为:让神经网络"更聪明",能处理复杂问题,而不是只会做简单的加法和乘法。
1. 为什么需要激活函数?
-
没有激活函数,网络像"线性模型"
- 如果没有激活函数,神经网络的输出就是输入的加权和,层与层之间是线性关系。
- 不管你堆多少层,最终的计算结果本质上都还是一个"线性模型"。这意味着网络只能画直线来解决问题,无法处理数据中的复杂关系。
-
引入激活函数后,网络能学会"弯曲的线"
- 激活函数可以将神经网络的输出变成非线性关系,使它能够拟合弯曲的线(复杂的决策边界)。
- 这样,网络才能解决复杂的任务,比如图像识别或语音识别。
2. 激活函数的作用,用比喻来理解
2.1 激活函数像"开关"
- 没有激活函数:就像灯泡的开关一直开着,灯光没有任何变化。
- 有了激活函数:就像灯泡开关根据情况灵活开关,可以控制灯光的强弱或模式。
3. 通俗解释几种常见激活函数
3.1 Sigmoid 函数
- 形状:像一个"S"型的曲线。
- 作用:把输出压缩到 0 和 1 之间。
- 通俗比喻:像"温和型开关",输出值像调光灯逐渐亮起或熄灭。
3.2 ReLU 函数
- 形状:大于0输出自己,小于0输出0。
- 作用:快速引入非线性,同时抑制负数部分。
- 通俗比喻:像"单向门",只允许有用的正数通过,过滤掉负数。
3.3 Tanh 函数
- 形状:类似Sigmoid,但范围是 -1 到 1。
- 作用:对称地压缩值,更适合归一化数据。
- 通俗比喻:像一个"温和的音量调节器",既能让声音放大(正值),也能让声音降低(负值)