开发也能看懂的大模型:FNN

609 阅读8分钟

前情提要:后端开发自学AI知识,内容比较浅显,偏实战;仅适用于入门了解,解决日常沟通障碍。

前馈神经网络是最基础的一类人工神经网络结构,也是深度学习的起点。它是由多层人工神经元按照顺序连接组成的模型,信息从输入层逐步向输出层传递(前向传播),没有循环或反馈。

1. FNN的基本结构

1.1 网络层组成

  1. 输入层

    • 接受原始数据作为输入。
    • 例如:对图像数据来说,输入层是图像像素值。
  2. 隐藏层

    • 包含一个或多个神经元层,逐层提取特征。
    • 神经元之间通过权重连接。
    • 每个隐藏层使用激活函数为数据引入非线性。
  3. 输出层

    • 最后一层神经元,将计算结果输出。

    • 输出的形式取决于任务:

      • 分类任务:输出类别概率。
      • 回归任务:输出连续值。

1.2 神经元的工作机制

image.png

2. FNN的特性

2.1 信息流动

  • 数据从输入层开始,经过每个隐藏层,最后到达输出层。
  • 没有循环或反馈,信息仅向前传播。

2.2 可扩展性

  • 层数和每层神经元数量可以调整。

    • 浅层网络:一两个隐藏层,适合简单任务。
    • 深层网络:多层隐藏层,适合复杂任务。

2.3 模型优化

  • 损失函数用于衡量预测结果与真实值之间的差异。
  • 通过反向传播算法(Backpropagation)调整权重和偏置,最小化损失。

3. 训练过程

3.1 前向传播

  • 数据从输入层开始,通过层间权重和激活函数逐步计算,到达输出层。

3.2 计算损失

  • 使用损失函数衡量预测值与真实值的误差。

    • 例如:

      • 分类:交叉熵损失(Cross-Entropy Loss)。
      • 回归:均方误差(Mean Squared Error)。

3.3 反向传播

  • 通过梯度下降算法更新权重和偏置。

  • 过程:

    1. 根据误差计算损失函数的梯度。
    2. 梯度反向传播到每一层。
    3. 按照梯度下降方向更新参数。

3.4 迭代优化

  • 多次前向传播和反向传播,直到损失函数收敛或达到设定的训练轮次。

4. FNN的激活函数

激活函数是FNN的核心,用于引入非线性。常见激活函数包括:

  1. Sigmoid

    • 输出范围:0,10, 10,1。
    • 优点:适合概率输出。
    • 缺点:梯度消失问题。
  2. ReLU(Rectified Linear Unit)

    • 输出:max⁡(0,x)\max(0, x)max(0,x)。
    • 优点:计算简单,缓解梯度消失。
    • 缺点:神经元可能“死亡”。
  3. Tanh

    • 输出范围:−1,1-1, 1−1,1。
    • 优点:比Sigmoid更适合归一化数据。
    • 缺点:仍有梯度消失问题。
  4. Softmax

    • 用于多分类任务,将输出转为概率分布。

应用场景

  1. 分类问题

    • 判断邮件是否为垃圾邮件。
    • 预测信用卡交易是否欺诈。
  2. 回归问题

    • 房价预测。
    • 销售额预测。
  3. 简单模式识别

    • 用户行为分析。
    • 客户分层。

FNN 使用案例:预测学生考试成绩

1. 问题描述

我们用一个简单的前馈神经网络(FNN)来预测学生的考试成绩,基于以下特征:

  • 学习时间
  • 睡眠时间
  • 学校支持情况
  • 家庭环境

数据样式

schoolsexageaddressfamsizePstatusMeduFedutraveltimestudytimefailuresschoolsupfamsuppaidactivitiesnurseryhigherinternetromanticfamrelfreetimegooutDalcWalchealthabsencesG1G2G3
GPF18UGT3A44220yesnononoyesyesyesno4341136566
MSM17ULE3T11123noyesyesnonoyesnoyes5432334556

数据样式解读

  1. 每行记录一个学生的特征与成绩。
  2. 数据中既包含 数值型特征(如年龄、成绩、学习时间),也包含 分类型特征(如学校、性别、家庭规模)。
  3. 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 模型设计
  1. 输入层:接收 32 个输入特征。
  2. 隐藏层:两层,每层有 64 个神经元,激活函数为 ReLU。
  3. 输出层:1 个节点,预测最终成绩,激活函数为线性。
3.3 训练与验证
  • 损失函数:均方误差(MSE)。
  • 优化器:Adam。
  • 数据划分:训练集 80%,验证集 20%。

4. 示例代码(使用 Python 和 Keras)

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:训练与验证损失

image.png

  • 内容:展示训练过程中模型损失(MSE)的变化。
  • 解读:训练损失与验证损失逐渐下降,趋于收敛,验证损失更能反映模型在未知数据上的表现。

图 2:真实值 vs. 预测值

image.png

  • 内容:散点图展示测试集中的真实成绩与预测成绩的对比。
  • 解读:点越接近对角线(理想线),说明模型的预测越准确。

图 3:误差分布

image.png

  • 内容:误差(预测值减真实值)的直方图。
  • 解读:误差分布接近零且对称,说明模型表现良好。若误差偏离过多或呈明显非对称分布,则需优化模型。

评估指标

image.png

  • MSE 反映误差的平方和,越小越好。
  • MAE 是实际误差的平均值,更易解读。

模型性能

  • 如果 MAE 小于 2,说明模型对成绩预测的偏差较小,可用于实际应用。
  • 如需更高的精度,可考虑优化数据预处理、调整模型结构或进行超参数调优。

补充:通俗解释激活函数

可以简单理解为:让神经网络“更聪明”,能处理复杂问题,而不是只会做简单的加法和乘法。

1. 为什么需要激活函数?

  1. 没有激活函数,网络像“线性模型”

    • 如果没有激活函数,神经网络的输出就是输入的加权和,层与层之间是线性关系。
    • 不管你堆多少层,最终的计算结果本质上都还是一个“线性模型”。这意味着网络只能画直线来解决问题,无法处理数据中的复杂关系。
  2. 引入激活函数后,网络能学会“弯曲的线”

    • 激活函数可以将神经网络的输出变成非线性关系,使它能够拟合弯曲的线(复杂的决策边界)。
    • 这样,网络才能解决复杂的任务,比如图像识别或语音识别。

2. 激活函数的作用,用比喻来理解

2.1 激活函数像“开关”

  • 没有激活函数:就像灯泡的开关一直开着,灯光没有任何变化。
  • 有了激活函数:就像灯泡开关根据情况灵活开关,可以控制灯光的强弱或模式。

3. 通俗解释几种常见激活函数

3.1 Sigmoid 函数

  • 形状:像一个“S”型的曲线。
  • 作用:把输出压缩到 0 和 1 之间。
  • 通俗比喻:像“温和型开关”,输出值像调光灯逐渐亮起或熄灭。

3.2 ReLU 函数

  • 形状:大于0输出自己,小于0输出0。
  • 作用:快速引入非线性,同时抑制负数部分。
  • 通俗比喻:像“单向门”,只允许有用的正数通过,过滤掉负数。

3.3 Tanh 函数

  • 形状:类似Sigmoid,但范围是 -1 到 1。
  • 作用:对称地压缩值,更适合归一化数据。
  • 通俗比喻:像一个“温和的音量调节器”,既能让声音放大(正值),也能让声音降低(负值)