大家好!最近很多朋友问我:“想入门深度学习,可神经网络总觉得抽象,怎么才能亲手搭一个出来?” 其实不用怕 —— 今天就用TensorFlow/Keras(最适合新手的深度学习框架),带大家从 0 到 1 搭建一个能识别手写数字的神经网络,全程附可运行代码,看完跟着敲一遍,你就能入门神经网络搭建!​

一、先搞懂:神经网络到底是什么?​

简单说,神经网络是模仿人脑神经元结构设计的 “计算模型”,核心是通过 “层” 的堆叠,让计算机从数据中学习规律。比如今天要做的 “手写数字识别”,就是让模型从成千上万张手写数字图片中,学会 “哪些像素组合对应 0,哪些对应 1……”​

它的核心组成很简单:​

  1. 输入层:接收原始数据(比如手写数字图片的像素值);​
  1. 隐藏层:对输入数据做 “复杂运算”(比如加权、激活),是模型 “学习” 的核心;​
  1. 输出层:给出最终结果(比如判断图片是 0-9 中哪个数字的概率);​
  1. 激活函数:给模型注入 “非线性能力”(比如 ReLU、Softmax),让模型能学习复杂规律。​

二、实战准备:3 分钟搭好环境​

今天用TensorFlow 2.x(自带 Keras 高层 API,不用写复杂底层代码),先搞定环境:​

  1. 安装 TensorFlow:打开终端 / 命令提示符,输入以下命令(需要先装 Python 3.8+):​

TypeScript取消自动换行复制

  1. 验证安装:打开 Python 终端,输入import tensorflow as tf,不报错就说明装好了。​

三、核心实战:搭建手写数字识别神经网络​

我们用经典的MNIST 数据集(包含 6 万张训练用手写数字图、1 万张测试图,每张图是 28x28 像素的黑白图),目标是让模型识别图中的数字(0-9)。​

步骤 1:导入需要的库​

先把工具包导入,就像做饭前把锅碗瓢盆准备好:​

TypeScript取消自动换行复制

import tensorflow as tf​

from tensorflow.keras import layers, models # 用来搭神经网络的层和模型​

import matplotlib.pyplot as plt # 用来画图​

步骤 2:加载并查看数据集​

MNIST 数据集是 TensorFlow 自带的,直接调用 API 就能加载,不用自己找数据:​

TypeScript取消自动换行复制

# 加载MNIST数据集(第一次运行会自动下载,后续直接用本地文件)​

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()​

# 查看数据格式:理解数据长什么样​

print("训练集图片形状:", x_train.shape) # 输出 (60000, 28, 28) → 6万张图,每张28x28像素​

print("训练集标签形状:", y_train.shape) # 输出 (60000,) → 6万张图对应的数字(0-9)​

print("测试集图片形状:", x_test.shape) # 输出 (10000, 28, 28) → 1万张测试图​

print("测试集标签形状:", y_test.shape) # 输出 (10000,) → 1万张测试图的真实数字​

# 画一张训练集的图看看(比如第0张)​

plt.imshow(x_train[0], cmap='gray') # 用灰度模式显示图片​

plt.title(f"真实数字:{y_train[0]}") # 标题显示真实数字​

plt.axis('off') # 隐藏坐标轴​

plt.show() # 弹出图片窗口​

运行后会看到一张手写 “5” 的图片(因为 x_train [0] 对应的标签 y_train [0] 是 5),这就是我们要让模型识别的数据。​

步骤 3:数据预处理(关键!模型训练的 “地基”)​

原始数据不能直接喂给模型,需要做 2 件事:​

  1. 展平图片:我们要搭的是 “全连接神经网络”,输入必须是 “一维向量”,而原始图片是 28x28 的二维矩阵,所以要把它展成 784 个元素的一维向量(28*28=784);​
  1. 归一化像素值:原始像素值是 0-255(黑色是 0,白色是 255),数值范围太大容易导致模型训练不稳定,所以归一化到 0-1 之间(除以 255)。​

代码实现:​

TypeScript取消自动换行复制

# 1. 展平图片:从(样本数, 28, 28) → (样本数, 784)​

x_train_flatten = x_train.reshape((60000, 28*28))​

x_test_flatten = x_test.reshape((10000, 28*28))​

# 2. 归一化:将像素值从0-255转为0-1​

x_train_norm = x_train_flatten / 255.0​

x_test_norm = x_test_flatten / 255.0​

# 3. 处理标签:将标签转为“独热编码”(适合多分类问题)​

# 比如标签5 → [0,0,0,0,0,1,0,0,0,0],方便计算损失​

y_train_onehot = tf.keras.utils.to_categorical(y_train, 10) # 10表示有10个类别(0-9)​

y_test_onehot = tf.keras.utils.to_categorical(y_test, 10)​

# 查看预处理后的数据格式​

print("预处理后训练集输入形状:", x_train_norm.shape) # 输出 (60000, 784)​

print("预处理后训练集标签形状:", y_train_onehot.shape) # 输出 (60000, 10)​

步骤 4:搭建神经网络模型​

用 Keras 的Sequential(序贯模型)搭建,就像 “搭积木” 一样一层一层堆上去:​

TypeScript取消自动换行复制

# 1. 初始化序贯模型​

model = models.Sequential()​

# 2. 添加输入层+第一个隐藏层(全连接层)​

# Dense:全连接层,units=128表示该层有128个神经元​

# activation='relu':用ReLU激活函数(解决梯度消失,计算快)​

# input_shape=(784,):输入数据的形状(784维向量)​

model.add(layers.Dense(units=128, activation='relu', input_shape=(784,)))​

# 3. 添加第二个隐藏层(可选,增加模型复杂度)​

model.add(layers.Dense(units=64, activation='relu'))​

# 4. 添加输出层​

# units=10:输出10个值(对应0-9的概率)​

# activation='softmax':将输出转为概率分布(所有值总和为1,方便判断类别)​

model.add(layers.Dense(units=10, activation='softmax'))​

# 查看模型结构​

model.summary()​

运行后会输出模型结构,类似这样:​

TypeScript取消自动换行复制

Model: "sequential"​

_________________________________________________________________​

Layer (type) Output Shape Param # ​

=================================================================​

dense (Dense) (None, 128) 100480 ​

dense_1 (Dense) (None, 64) 8256 ​

dense_2 (Dense) (None, 10) 650 ​

=================================================================​

Total params: 109,386​

Trainable params: 109,386​

Non-trainable params: 0​

“Param #” 是该层的参数数量,比如第一个隐藏层有 784*128 + 128 = 100480 个参数(每个输入神经元对应一个权重,加每个神经元的偏置)。​

步骤 5:编译模型(告诉模型 “怎么学”)​

编译时要指定 3 个关键参数:​

  1. 优化器(optimizer):控制模型如何调整参数来降低误差,选adam(自适应学习率,新手不用手动调参);​
  1. 损失函数(loss):衡量模型预测值和真实值的差距,多分类问题用categorical_crossentropy;​
  1. 评估指标(metrics):训练时看什么指标,选accuracy(准确率,即预测对的样本占比)。​

代码:​

TypeScript取消自动换行复制

model.compile(​

optimizer='adam',​

loss='categorical_crossentropy',​

metrics=['accuracy']​

)​

步骤 6:训练模型(让模型 “从数据中学习”)​

用训练集数据训练,同时用部分数据验证模型是否过拟合(“过拟合” 就是模型只学懂了训练数据,没学懂通用规律):​

TypeScript取消自动换行复制

# 开始训练:fit()方法​

history = model.fit(​

x=x_train_norm, # 训练集输入​

y=y_train_onehot, # 训练集标签​

epochs=5, # 训练轮次:整个训练集跑5遍​

batch_size=32, # 批次大小:每次用32个样本更新参数​

validation_split=0.1 # 用10%的训练数据做验证(看模型在未训练过的数据上的表现)​

)​

训练过程中会实时输出日志,类似这样:​

TypeScript取消自动换行复制

Epoch 1/5​

1688/1688 [==============================] - 5s 3ms/step - loss: 0.2574 - accuracy: 0.9251 - val_loss: 0.1147 - val_accuracy: 0.9668​

Epoch 2/5​

1688/1688 [==============================] - 4s 2ms/step - loss: 0.1047 - accuracy: 0.9687 - val_loss: 0.0893 - val_accuracy: 0.9732​

...​

  • loss/accuracy:训练集上的损失和准确率;​
  • val_loss/val_accuracy:验证集上的损失和准确率;​
  • 可以看到:随着训练轮次增加,损失在下降,准确率在上升,说明模型在进步。​

步骤 7:评估模型(看模型 “学的怎么样”)​

用测试集(模型从没见过的数据)评估泛化能力:​

TypeScript取消自动换行复制

# 用测试集评估​

test_loss, test_acc = model.evaluate(x_test_norm, y_test_onehot)​

print(f"测试集准确率:{test_acc:.4f}") # 输出类似 测试集准确率:0.9750 → 97.5%的测试图能识别对​

正常情况下,测试准确率能达到 97% 以上,对于一个简单的全连接网络来说,这个效果已经很好了。​

步骤 8:用模型做预测(让模型 “干活”)​

选一张测试图,让模型预测它是什么数字:​

TypeScript取消自动换行复制

# 1. 选一张测试图(比如第10张)​

test_image_index = 10​

test_image = x_test[test_image_index] # 原始28x28图片(用于画图)​

test_image_input = x_test_norm[test_image_index] # 预处理后的输入(用于预测)​

# 2. 模型预测:predict()返回概率分布(10个值,对应0-9的概率)​

prediction = model.predict(test_image_input.reshape(1, 784)) # 要把输入转成(1,784)(1个样本,784维)​

predicted_label = tf.argmax(prediction, axis=1).numpy()[0] # 取概率最大的索引,就是预测的数字​

true_label = y_test[test_image_index] # 真实数字​

# 3. 画图+显示结果​

plt.imshow(test_image, cmap='gray')​

plt.title(f"真实数字:{true_label} | 预测数字:{predicted_label}")​

plt.axis('off')​

plt.show()​

# 打印预测概率分布​

print("0-9的预测概率:")​

for i in range(10):​

print(f"数字{i}的概率:{prediction[0][i]:.4f}")​

运行后会看到:比如真实数字是 0,模型预测数字也是 0,且数字 0 的概率接近 1,其他数字的概率接近 0,说明模型预测得很准。​

四、进阶方向:让模型更强大​

如果想进一步提升准确率,可以尝试这些优化:​

  1. 增加隐藏层或神经元数量:比如把隐藏层改成units=256,或多加一层Dense(32, activation='relu');​
  1. 加入 dropout 防止过拟合:在隐藏层后加model.add(layers.Dropout(0.2))(随机让 20% 的神经元不工作,避免模型 “死记硬背”);​
  1. 用卷积神经网络(CNN):MNIST 是图像数据,CNN 比全连接网络更擅长处理图像,准确率能轻松达到 99% 以上(后续可以专门写一篇 CNN 的教程);​
  1. 调整超参数:比如epochs=10(多训练几轮)、batch_size=64(调整批次大小)。​

五、总结​

今天我们用不到 50 行核心代码,完成了神经网络的全流程:​

  1. 数据加载→预处理(展平 + 归一化);​
  1. 模型搭建(Sequential+Dense 层);​
  1. 模型编译(optimizer+loss+metrics);​
  1. 训练→评估→预测。​

其实神经网络没那么抽象,新手从简单的全连接网络入手,熟悉流程后再学复杂模型(CNN、RNN)会更轻松。赶紧把代码复制到本地跑一遍,动手实践才是最好的学习方式!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐