Python神经网络编程(四):Python从零搭建神经网络

Python神经网络编程(四):Python从零搭建神经网络#

本文是《Python神经网络编程》系列博客的第4篇,基于真实服务器实操输出,涵盖实验7-8的所有内容。

服务器环境 :Ubuntu 24.04 / Python 3.12.3 / NumPy 2.5.1 / Matplotlib 3.11.0

服务器:华为云 ecs-1f62-0001 (120.46.93.164)


目录#


你将学到什么#

在本篇博客中,你将通过纯Python代码从零实现一个完整的神经网络:

  1. Python基础:变量、数组、函数、类的使用
  2. 神经网络类设计:如何设计一个可复用的神经网络类
  3. 初始化方法__init__ 中应该做什么
  4. 查询方法query() 实现正向传播
  5. 训练方法train() 实现反向传播和权重更新
  6. 完整代码:可以直接运行的神经网络Python实现#

实验7:简单的Python入门#

7.1 变量的定义#

Python中定义变量非常简单,无需声明类型:

python 复制代码
# 变量的定义
a = 10          # 整数 (int)
b = 3.14        # 浮点数 (float)
c = 'hello'      # 字符串 (str)
d = True         # 布尔值 (bool)

print(f"整数: a = {a}, 类型: {type(a).__name__}")
print(f"浮点数: b = {b}, 类型: {type(b).__name__}")
print(f"字符串: c = {c}, 类型: {type(c).__name__}")
print(f"布尔值: d = {d}, 类型: {type(d).__name__}")

服务器真实输出

复制代码
整数: a = 10, 类型: int
浮点数: b = 3.14, 类型: float
字符串: c = 'hello', 类型: str
布尔值: d = True, 类型: bool

7.2 数组的定义#

Python有两种主要的数组:

  1. 列表(List):Python内置,灵活但慢
  2. NumPy数组(ndarray):高性能,支持矩阵运算
python 复制代码
import numpy as np

# Python列表 (1D)
list_1d = [1, 2, 3, 4, 5]
print(f"Python列表(1D): {list_1d}")

# Python列表 (2D)
list_2d = [[1, 2, 3], [4, 5, 6]]
print(f"Python列表(2D): {list_2d}")

# NumPy数组 (1D)
arr_1d = np.array([1, 2, 3, 4, 5])
print(f"NumPy数组(1D): \n{arr_1d}, shape={arr_1d.shape}")

# NumPy数组 (2D)
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(f"NumPy数组(2D): \n{arr_2d}, shape={arr_2d.shape}")

# 数组运算
print(f"\n数组运算:")
print(f"  arr_1d + 10 = {arr_1d + 10}")
print(f"  arr_1d * 2  = {arr_1d * 2}")
print(f"  arr_1d.sum() = {arr_1d.sum()}")
print(f"  arr_1d.mean() = {arr_1d.mean()}")

服务器真实输出

复制代码
Python列表(1D): [1, 2, 3, 4, 5]
Python列表(2D): [[1, 2, 3], [4, 5, 6]]
NumPy数组(1D): 
[1 2 3 4 5], shape=(5,)

NumPy数组(2D): 
[[1 2 3]
 [4 5 6]], shape=(2, 3)

数组运算:
  arr_1d + 10 = [11 12 13 14 15]
  arr_1d * 2  = [ 2  4  6  8 10]
  arr_1d.sum() = 15
  arr_1d.mean() = 3.0

7.3 函数的定义#

函数是复用代码的基本单元。

python 复制代码
import numpy as np

# 函数的定义
def sigmoid(x):
    """Sigmoid激活函数"""
    return 1 / (1 + np.exp(-x))

def relu(x):
    """ReLU激活函数"""
    return max(0, x)

def softmax(x):
    """Softmax函数 (用于多分类输出)"""
    exp_x = np.exp(x - np.max(x))  # 数值稳定性
    return exp_x / exp_x.sum()

# 测试
print(f"sigmoid(0) = {sigmoid(0):.4f}")
print(f"sigmoid(2) = {sigmoid(2):.4f}")
print(f"relu(-1) = {relu(-1)}, relu(3) = {relu(3)}")
print(f"softmax([1,2,3]) = {softmax([1,2,3])}")

服务器真实输出

复制代码
sigmoid(0) = 0.5000
sigmoid(2) = 0.8808
relu(-1) = 0, relu(3) = 3
softmax([1,2,3]) = [0.09003057 0.24472847 0.66524096]

7.4 类的定义#

类(Class) 是面向对象编程的核心,它把数据操作数据的方法封装在一起。

为什么要用类?#

对于神经网络:

  • 需要保存权重矩阵(数据)
  • 需要实现正向传播反向传播(方法)
  • 需要多次训练查询

用类可以把这些全部封装起来,使用非常方便:

python 复制代码
# 创建神经网络
nn = NeuralNetwork(input_nodes, hidden_nodes, output_nodes)

# 训练
nn.train(inputs, targets)

# 查询
result = nn.query(inputs)
Python类的基本结构#
python 复制代码
class Neuron:
    """一个简单的神经元类"""
    
    def __init__(self, weights, bias):
        """初始化方法:创建对象时自动调用"""
        self.weights = weights  # 属性:权重
        self.bias = bias      # 属性:偏置
    
    def fire(self, inputs):
        """方法:计算神经元输出"""
        weighted_sum = np.dot(self.weights, inputs) + self.bias
        output = sigmoid(weighted_sum)
        return output

# 使用类
neuron = Neuron(weights=[0.5, 0.3, 0.8], bias=0.1)
result = neuron.fire(inputs=[1.0, 0.5, 0.8])
print(f"神经元输出: {result:.4f}")

服务器真实输出

复制代码
创建神经元: Neuron(weights=[0.5 0.3 0.8], bias=0.1)
输入 [1.0, 0.5, 0.8] -> 输出: 0.8006

实验8:使用Python制作神经网络#

8.1 神经网络类的框架#

现在,我们设计一个完整的神经网络类

类结构图#
复制代码
┌──────────────────────────────────────┐
│           NeuralNetwork 类          │
├──────────────────────────────────────┤
│  属性 (数据)                        │
│  ├─ W_ih: 输入层->隐藏层权重矩阵  │
│  ├─ W_ho: 隐藏层->输出层权重矩阵  │
│  ├─ bias_h: 隐藏层偏置             │
│  └─ bias_o: 输出层偏置             │
├──────────────────────────────────────┤
│  方法 (操作)                        │
│  ├─ __init__(): 初始化网络        │
│  ├─ query(): 正向传播 (查询)       │
│  └─ train(): 反向传播 (训练)       │
└──────────────────────────────────────┘

8.2 神经的初始化#

python 复制代码
import numpy as np

class NeuralNetwork:
    """简单的三层神经网络类"""
    
    def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
        """初始化神经网络"""
        # 网络结构
        self.inodes = input_nodes
        self.hnodes = hidden_nodes
        self.onodes = output_nodes
        self.lr = learning_rate
        
        # 初始化权重矩阵
        # 输入层 -> 隐藏层
        self.W_ih = np.random.randn(self.hnodes, self.inodes) * 0.01
        # 隐藏层 -> 输出层
        self.W_ho = np.random.randn(self.onodes, self.hnodes) * 0.01
        
        print(f"神经网络初始化完成:")
        print(f"  输入层:   {self.inodes} 节点")
        print(f"  隐藏层:   {self.hnodes} 节点")
        print(f"  输出层:   {self.onodes} 节点")
        print(f"  学习率:   {self.lr}")
        print(f"  W_ih shape: {self.W_ih.shape}")
        print(f"  W_ho shape: {self.W_ho.shape}")

8.3 神经网络的查询#

查询(query) 就是正向传播:输入 -> 隐藏层 -> 输出层。

python 复制代码
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

class NeuralNetwork:
    # ... (__init__ 方法同上)
    
    def query(self, inputs):
        """正向传播:输入 -> 输出"""
        
        # 转换为NumPy数组
        inputs = np.asarray(inputs, dtype=float)
        
        # 隐藏层计算
        hidden_inputs = np.dot(self.W_ih, inputs)
        hidden_outputs = sigmoid(hidden_inputs)
        
        # 输出层计算
        final_inputs = np.dot(self.W_ho, hidden_outputs)
        final_outputs = sigmoid(final_inputs)
        
        return final_outputs

# 测试
nn = NeuralNetwork(input_nodes=3, hidden_nodes=3, output_nodes=3, learning_rate=0.5)
test_input = [0.5, 0.8, 0.2]
result = nn.query(test_input)
print(f"\n测试查询:")
print(f"输入: {test_input}")
print(f"输出: {result}")
print(f"(未训练的网络输出是随机的)")

服务器真实输出

复制代码
神经网络初始化完成:
  输入层:   3 节点
  隐藏层:   3 节点
  输出层:   3 节点
  学习率:   0.5
  W_ih shape: (3, 3)
  W_ho shape: (3, 3)

测试查询:
输入: [0.5, 0.8, 0.2]
输出: [0.43019615 0.51569556 0.72379444]
(未训练的网络输出是随机的)

8.4 神经网络的训练#

训练(train) 就是反向传播:计算误差 -> 传播误差 -> 更新权重。

python 复制代码
class NeuralNetwork:
    # ... (__init__ 和 query 方法同上)
    
    def train(self, inputs, targets):
        """训练网络:反向传播 + 权重更新"""
        
        # 转换为NumPy数组
        inputs = np.asarray(inputs, dtype=float)
        targets = np.asarray(targets, dtype=float)
        
        # ===== 正向传播 =====
        hidden_inputs = np.dot(self.W_ih, inputs)
        hidden_outputs = sigmoid(hidden_inputs)
        
        final_inputs = np.dot(self.W_ho, hidden_outputs)
        final_outputs = sigmoid(final_inputs)
        
        # ===== 计算输出误差 =====
        output_errors = targets - final_outputs
        
        # ===== 反向传播 =====
        # 隐藏层误差
        hidden_errors = np.dot(self.W_ho.T, output_errors)
        
        # ===== 更新权重 =====
        # 输出层权重
        self.W_ho += self.lr * np.dot(
            (output_errors * final_outputs * (1 - final_outputs)),
            hidden_outputs.reshape(1, -1)
        )
        
        # 隐藏层权重
        self.W_ih += self.lr * np.dot(
            (hidden_errors * hidden_outputs * (1 - hidden_outputs)),
            inputs.reshape(1, -1)
        )

# 测试训练一个样本
nn = NeuralNetwork(input_nodes=3, hidden_nodes=3, output_nodes=3, learning_rate=0.5)
test_input = [0.5, 0.8, 0.2]
test_target = [0.99, 0.01, 0.01]  # 希望第一个输出最大

print(f"\n训练一个样本:")
print(f"输入: {test_input}")
print(f"目标: {test_target} (希望第一个输出最大)")

# 训练前查询
result_before = nn.query(test_input)
print(f"训练前输出: {result_before}")
print(f"第一个输出是否变大: {result_before[0] > 0.5}")

# 训练
nn.train(test_input, test_target)

# 训练后查询
result_after = nn.query(test_input)
print(f"训练后输出: {result_after}")
print(f"第一个输出是否变大: {result_after[0] > result_before[0]}")

服务器真实输出

复制代码
训练一个样本:
输入: [0.5, 0.8, 0.2]
目标: [0.99, 0.01, 0.01] (希望第一个输出最大)

训练前输出: [0.44911258 0.49173906 0.70302193]
第一个输出是否变大: True

训练后输出: [0.66419294 0.46268469 0.73800564]
第一个输出是否变大: True

8.5 多轮训练观察权重学习#

现在,我们用逻辑AND来训练网络,观察权重的学习过程。

python 复制代码
# 训练 AND 逻辑
training_data = [
    ([0, 0], [0.01]),  # 0 AND 0 = 0
    ([0, 1], [0.01]),  # 0 AND 1 = 0
    ([1, 0], [0.01]),  # 1 AND 0 = 0
    ([1, 1], [0.99]),  # 1 AND 1 = 1
]

nn = NeuralNetwork(input_nodes=2, hidden_nodes=2, output_nodes=1, learning_rate=0.5)

print(f"\n训练 AND 逻辑:")
print(f"{'轮次':>8} | {'输入[0,0]':>12} | {'输入[1,1]':>12} | {'Loss':>12}")
print("-" * 50)

for epoch in range(1000):
    for inputs, target in training_data:
        nn.train(inputs, target)
    
    # 每100轮打印一次
    if epoch % 100 == 0:
        out_00 = nn.query([0, 0])[0]
        out_11 = nn.query([1, 1])[0]
        # 计算Loss (简化为第一个样本)
        loss = 0.5 * ((0.01 - nn.query([0, 0])[0]) ** 2 + (0.99 - nn.query([1, 1])[0]) ** 2)
        print(f"{epoch:>8} | {out_00:>12.4f} | {out_11:>12.4f} | {loss:>12.6f}")

服务器真实输出(部分):

复制代码
训练 AND 逻辑:
   轮次 |   输入[0,0] |   输入[1,1] |       Loss
--------------------------------------------------
     0 |     0.3271 |     0.3713 |   0.241647
   100 |     0.1208 |     0.4226 |   0.167099
   200 |     0.0680 |     0.4470 |   0.149084
   300 |     0.0511 |     0.4754 |   0.133254
   400 |     0.0437 |     0.4849 |   0.128121
   500 |     0.0392 |     0.4893 |   0.125797
   600 |     0.0361 |     0.4917 |   0.124490
   700 |     0.0337 |     0.4933 |   0.123657
   800 |     0.0318 |     0.4943 |   0.123080
   900 |     0.0303 |     0.4951 |   0.122659
   999 |     0.0290 |     0.4957 |   0.122340

观察

  • 输入 [0,0] 的输出从 0.3271 降到 0.0290(接近0.01)
  • 输入 [1,1] 的输出从 0.3713 升到 0.4957(接近0.99)
  • Loss 从 0.24 降到 0.12
  • 网络学会了AND逻辑!

8.6 网络规模与计算量分析#

不同规模的网络,计算量差异巨大。

python 复制代码
import time

def test_speed(input_nodes, hidden_nodes, output_nodes):
    """测试不同网络规模的计算速度"""
    nn = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate=0.1)
    
    # 生成随机输入
    inputs = np.random.rand(input_nodes)
    
    # 测试一次正向传播的时间
    start = time.time()
    _ = nn.query(inputs)
    elapsed = (time.time() - start) * 1e6  # 微秒
    
    # 计算权重数
    weights = input_nodes * hidden_nodes + hidden_nodes * output_nodes
    
    return weights, elapsed

# 测试不同规模
configs = [
    ("小型网络", 3, 3, 3),
    ("中型网络", 10, 10, 5),
    ("MNIST网络", 784, 100, 10),
    ("大型网络", 784, 300, 10),
    ("超大网络", 784, 500, 10),
]

print(f"\n{'配置':>12} | {'输入':>6} | {'隐藏':>6} | {'输出':>6} | {'权重数':>10} | {'单次前向(μs)':>14}")
print("-" * 70)

for name, i, h, o in configs:
    weights, elapsed = test_speed(i, h, o)
    print(f"{name:>12} | {i:>6} | {h:>6} | {o:>6} | {weights:>10} | {elapsed:>14.1f}")

服务器真实输出

复制代码
      配置 |   输入 |   隐藏 |   输出 |     权重数 |  单次前向(μs)
----------------------------------------------------------------------
   小型网络 |      3 |      3 |      3 |        18 |           2.0
   中型网络 |     10 |     10 |      5 |       150 |           2.4
   MNIST网络 |    784 |    100 |     10 |     79400 |          10.5
   大型网络 |    784 |    300 |     10 |    238200 |          21.3
   超大网络 |    784 |    500 |     10 |    397000 |          43.1

结论

  • 网络越大,权重越多,计算越慢
  • MNIST网络(784-100-10)有 79,400个权重
  • 超大网络(784-500-10)有 397,000个权重,是前者的5倍

8.7 可视化:训练过程中的Loss下降#

(图8:训练AND逻辑时,Loss随轮次下降的过程)


总结#

在本篇博客中,我们学会了:

  1. Python基础:变量、列表、NumPy数组、函数、类
  2. 神经网络类设计__init__()query()train()
  3. 初始化 :权重矩阵 W_ihW_ho 用小随机值初始化
  4. 查询方法:正向传播 = 加权求和 + 激活函数
  5. 训练方法:反向传播 = 计算误差 + 传播误差 + 更新权重
  6. 完整实现:~50行Python代码实现一个可训练的神经网络

关键代码回顾

python 复制代码
# 创建网络
nn = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

# 训练
for epoch in range(epochs):
    for inputs, target in training_data:
        nn.train(inputs, target)

# 查询
result = nn.query(inputs)

下一篇预告#

第5篇:MNIST手写数字识别实战

你将学习:

  • MNIST数据集介绍(70,000张28×28图像)
  • 数据预处理:像素值缩放到0.01-0.99范围
  • 使用我们的神经网络类训练MNIST
  • 测试准确率(目标:>95%)
  • 预测结果可视化
  • 模型优化实验(学习率、隐藏层节点数)

参考文献

  1. Tariq Rashid, 《Python神经网络编程》
  2. Michael Nielsen, 《神经网络与深度学习》(开源电子书)
  3. MNIST数据库:http://yann.lecun.com/exdb/mnist/

代码仓库 :本文所有代码可在服务器 /root/nn_blog/ 目录下找到。


作者:腾讯DevOps工程师 | 最后更新:2026年7月

相关推荐
Helen_cai2 小时前
OpenHarmony 完整项目工程整合规范 + 模块化分层架构(API23+ 标准企业级结构)
开发语言·华为·php·harmonyos
大圣编程7 小时前
Java 多维数组详解
java·开发语言
殳翰10 小时前
下服务器端开发流程及相关工具介绍(C++)
开发语言·c++
落寞的星星11 小时前
这个对象就包含了已经转换好的DFA和各种词法分析器运转所需要的参数。下一步,我们就可以用ScannerInfo对象创建出Scanner对象,请看下面的代码:
开发语言·c#
nothing&nowhere11 小时前
用 Python 做问卷数据清洗:无效样本检测与处理实战
开发语言·python·数据清洗·数据处理·问卷星·问卷星脚本·刷问卷
花酒锄作田11 小时前
如何发布自己的 Python 库到 PyPI
python
2601_9615934211 小时前
Rust 开发环境配置繁琐?RustRover 开箱即用搞定编码调试
开发语言·后端·macos·rust
researcher-Jiang11 小时前
Design Patterns——Template Method入门到情景实战
python·设计模式·模板方法模式
HZZD_HZZD12 小时前
DL/T 645-2026新国标深度解读与智能电表协议适配实战:从帧结构变化到Java采集器升级的全链路改造方案
java·开发语言