机器学习

一.TensorFlow/Keras 的手写数字(0 和 1)神经网络

1.模型构建代码

复制代码
# 导入依赖
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

# 构建模型
model = Sequential(
    [
        tf.keras.Input(shape=(400,)),  # 输入层:指定输入维度为400(20×20图像展平)
        Dense(25, activation="sigmoid", name="layer1"),  # 隐藏层1:25个神经元,sigmoid激活
        Dense(15, activation="sigmoid", name="layer2"),  # 隐藏层2:15个神经元,sigmoid激活
        Dense(1, activation="sigmoid", name="layer3")   # 输出层:1个神经元,输出二分类概率
    ], name="my_model"
)

# 查看模型结构
model.summary()

关键细节说明

  • tf.keras.Input(shape=(400,))显式指定输入维度,让 TensorFlow 提前计算各层权重形状,方便调试参数数量。
  • Dense 层:全连接层,参数为「神经元数量 + 激活函数」,自动处理权重矩阵和偏置向量。
  • activation="sigmoid"
    • 隐藏层:引入非线性,让网络能学习复杂的图像特征
    • 输出层:输出 0~1 之间的概率值,适配二分类任务

2. 模型结构与参数解析

model.summary() 输出结果:

Layer (type) Output Shape Param # 计算逻辑
dense (Dense) (None, 25) 10025 400×25+25=10025(输入 400 维,输出 25 维,含偏置)
dense_1 (Dense) (None, 15) 390 25×15+15=390(上一层输出 25 维,输出 15 维,含偏置)
dense_2 (Dense) (None, 1) 16 15×1+1=16(上一层输出 15 维,输出 1 维,含偏置)
Total params - 10431 所有层参数总数之和

权重维度验证

复制代码
# 获取各层权重与偏置
layer1, layer2, layer3 = model.layers
W1, b1 = layer1.get_weights()
W2, b2 = layer2.get_weights()
W3, b3 = layer3.get_weights()

print(f"W1 shape = {W1.shape}, b1 shape = {b1.shape}")  # (400, 25), (25,)
print(f"W2 shape = {W2.shape}, b2 shape = {b2.shape}")  # (25, 15), (15,)
print(f"W3 shape = {W3.shape}, b3 shape = {b3.shape}")  # (15, 1), (1,)
  • 权重矩阵 W:维度为「上一层神经元数 × 当前层神经元数」
  • 偏置向量 b:维度为「当前层神经元数」,和 TensorFlow 内部实现保持一致

3. 模型编译与训练

复制代码
# 编译模型:定义损失函数、优化器和评估指标
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(),  # 二分类任务专用损失函数
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),  # Adam优化器,自适应学习率
    metrics=['accuracy']  # 训练时同时输出准确率
)

# 训练模型
model.fit(
    X, y,  # 训练数据和标签
    epochs=20,  # 训练轮数
    batch_size=32,  # 每次更新参数使用32个样本
    verbose=1  # 显示训练进度条
)

关键概念

  • BinaryCrossentropy:二分类交叉熵损失,衡量模型预测概率和真实标签的差距,越小越好
  • Adam:比传统 SGD 更稳定的优化器,能自动调整学习率,收敛速度更快
  • epochs:训练轮数,轮数过少模型欠拟合,过多可能过拟合(本次作业 20 轮即可)

4. 模型预测与结果解读

单个样本预测

复制代码
# 预测数字0的样本(X[0])
prediction = model.predict(X[0].reshape(1, 400))  # 单个样本需重塑为二维数组(1,400)
print(f"predicting a zero: {prediction}")  # 输出概率,如[[0.0157]]

# 预测数字1的样本(X[500])
prediction = model.predict(X[500].reshape(1, 400))
print(f"predicting a one: {prediction}")  # 输出概率,如[[0.9814]]

# 概率转标签(0.5为阈值)
if prediction >= 0.5:
    yhat = 1
else:
    yhat = 0
print(f"prediction after threshold: {yhat}")

批量样本可视化预测

复制代码
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

m, n = X.shape
fig, axes = plt.subplots(8, 8, figsize=(8, 8))
fig.tight_layout(pad=0.1, rect=[0, 0.03, 1, 0.92])  # 调整布局,给标题留空间

for i, ax in enumerate(axes.flat):
    # 随机选择样本
    random_index = np.random.randint(m)
    
    # 重塑为20×20图像并显示
    X_reshaped = X[random_index].reshape((20, 20)).T
    ax.imshow(X_reshaped, cmap='gray')
    
    # 模型预测
    pred = model.predict(X[random_index].reshape(1, 400))
    yhat = 1 if pred >= 0.5 else 0
    
    # 显示真实标签和预测结果(格式:真实标签,预测标签)
    ax.set_title(f"{y[random_index,0]},{yhat}")
    ax.set_axis_off()

fig.suptitle("Label, yhat", fontsize=16)
plt.show()
知识点 核心内容
Keras 模型构建 Sequential 按顺序堆叠层,Dense 实现全连接层
输入维度处理 20×20 图像展平为 400 维向量,模型输入层需匹配该维度
激活函数选择 隐藏层用 sigmoid 引入非线性,输出层用 sigmoid 输出概率
参数数量计算 每一层参数 = 上一层神经元数 × 当前层神经元数 + 当前层神经元数(偏置)
二分类任务流程 构建模型 → 编译(损失 + 优化器)→ 训练 → 预测(概率转标签)
模型调试 通过 summary()get_weights() 验证各层维度和参数

二. NumPy 模型实现(单样本前向传播)

1.单样本全连接层 my_dense 函数

复制代码
import numpy as np

def sigmoid(z):
    """Sigmoid激活函数"""
    return 1 / (1 + np.exp(-z))

def my_dense(a_in, W, b, g):
    """
    手动实现全连接层(单样本)
    Args:
        a_in (ndarray (n,)): 输入数据,单个样本,n个特征
        W (ndarray (n,j)): 权重矩阵,n个输入特征,j个输出单元
        b (ndarray (j,)): 偏置向量,j个输出单元
        g (callable): 激活函数(如sigmoid)
    Returns:
        a_out (ndarray (j,)): 层的输出,j个单元的激活值
    """
    units = W.shape[1]  # 获取输出单元数量
    a_out = np.zeros(units)  # 初始化输出
    
    # 遍历每个输出单元,手动计算加权和+激活
    for j in range(units):
        w = W[:, j]  # 取出第j个单元的权重向量(列向量)
        z = np.dot(w, a_in) + b[j]  # 加权和(点积+偏置)
        a_out[j] = g(z)  # 应用激活函数
    
    return a_out
  • 输入输出维度
    • a_in(n,),单样本特征向量(如 400 维的手写数字图像)
    • W(n, j),权重矩阵,每一列对应一个输出单元的权重
    • b(j,),偏置向量,每个元素对应一个输出单元的偏置
    • a_out(j,),层的输出,每个元素是对应单元的激活值
  • 计算逻辑 :对每个输出单元 j,计算:zj=wj⋅ain+bjaj=g(zj)其中 wj 是权重矩阵的第 j 列,g 是激活函数(本次作业用 sigmoid)。

测试代码与验证

复制代码
# 测试用例
x_tst = 0.1 * np.arange(1, 3, 1).reshape(2,)  # 1个样本,2个特征
W_tst = 0.1 * np.arange(1, 7, 1).reshape(2, 3)  # 2输入,3输出
b_tst = 0.1 * np.arange(1, 4, 1).reshape(3,)  # 3个输出单元的偏置

A_tst = my_dense(x_tst, W_tst, b_tst, sigmoid)
print(A_tst)  # 输出:[0.54735762 0.57932425 0.61063923]

2. 构建三层神经网络 my_sequential

复制代码
def my_sequential(x, W1, b1, W2, b2, W3, b3):
    """手动实现三层全连接神经网络(单样本前向传播)"""
    a1 = my_dense(x, W1, b1, sigmoid)  # 第一层:400→25
    a2 = my_dense(a1, W2, b2, sigmoid)  # 第二层:25→15
    a3 = my_dense(a2, W3, b3, sigmoid)  # 第三层:15→1(输出层)
    return a3

权重与偏置复用

直接从之前训练好的 TensorFlow 模型中提取参数,保证和 Keras 模型的权重一致:

复制代码
# 从TensorFlow模型中提取各层权重和偏置
W1_tmp, b1_tmp = layer1.get_weights()
W2_tmp, b2_tmp = layer2.get_weights()
W3_tmp, b3_tmp = layer3.get_weights()

单样本预测验证

复制代码
# 预测数字0的样本(X[0])
prediction = my_sequential(X[0], W1_tmp, b1_tmp, W2_tmp, b2_tmp, W3_tmp, b3_tmp)
yhat = 1 if prediction >= 0.5 else 0
print(f"yhat = {yhat}, label = {y[0,0]}")  # 输出:yhat = 0, label = 0

# 预测数字1的样本(X[500])
prediction = my_sequential(X[500], W1_tmp, b1_tmp, W2_tmp, b2_tmp, W3_tmp, b3_tmp)
yhat = 1 if prediction >= 0.5 else 0
print(f"yhat = {yhat}, label = {y[500,0]}")  # 输出:yhat = 1, label = 1

3. 批量样本可视化对比(NumPy vs TensorFlow)

复制代码
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

m, n = X.shape
fig, axes = plt.subplots(8, 8, figsize=(8, 8))
fig.tight_layout(pad=0.1, rect=[0, 0.03, 1, 0.92])

for i, ax in enumerate(axes.flat):
    random_index = np.random.randint(m)
    # 重塑图像并显示
    X_reshaped = X[random_index].reshape((20, 20)).T
    ax.imshow(X_reshaped, cmap='gray')
    
    # NumPy模型预测
    my_pred = my_sequential(X[random_index], W1_tmp, b1_tmp, W2_tmp, b2_tmp, W3_tmp, b3_tmp)
    my_yhat = int(my_pred >= 0.5)
    
    # TensorFlow模型预测
    tf_pred = model.predict(X[random_index].reshape(1, 400), verbose=0)
    tf_yhat = int(tf_pred >= 0.5)
    
    # 显示真实标签+两个模型的预测结果
    ax.set_title(f"{y[random_index,0]},{tf_yhat},{my_yhat}")
    ax.set_axis_off()

fig.suptitle("Label, yhat TensorFlow, yhat NumPy", fontsize=16)
plt.show()

三.向量化 NumPy 模型实现(批量样本优化)

1. 向量化全连接层 my_dense_v 函数

复制代码
def my_dense_v(A_in, W, b, g):
    """
    向量化实现全连接层(批量样本)
    Args:
        A_in (ndarray (m,n)): 输入数据,m个样本,n个特征
        W (ndarray (n,j)): 权重矩阵,n个输入特征,j个输出单元
        b (ndarray (1,j)): 偏置向量,j个输出单元(广播适配批量)
        g (callable): 激活函数(如sigmoid)
    Returns:
        A_out (ndarray (m,j)): 层的输出,m个样本,j个单元的激活值
    """
    Z = np.matmul(A_in, W) + b  # 矩阵乘法(批量加权和)+ 广播偏置
    A_out = g(Z)  # 应用激活函数
    return A_out
  • 维度变化(批量支持)
    • A_in(m, n),m 个样本,每个样本 n 个特征(如 1000×400 的手写数字数据集)
    • W(n, j),权重矩阵,和单样本版本一致
    • b(1, j),偏置向量,通过 NumPy 广播适配批量维度
    • A_out(m, j),层的输出,m 个样本,每个样本 j 个单元的激活值
  • 向量化计算逻辑 :一次性计算所有样本、所有单元的加权和:Z=Ain⋅W+bAout=g(Z)其中矩阵乘法 np.matmul(A_in, W) 同时完成了所有样本的点积运算,避免了 for 循环。

测试代码与验证

复制代码
# 测试用例
X_tst = 0.1 * np.arange(1, 9, 1).reshape(4, 2)  # 4个样本,2个特征
W_tst = 0.1 * np.arange(1, 7, 1).reshape(2, 3)  # 2输入,3输出
b_tst = 0.1 * np.arange(1, 4, 1).reshape(1, 3)  # 3个输出单元的偏置(适配批量)

A_tst = my_dense_v(X_tst, W_tst, b_tst, sigmoid)
print(A_tst)

输出结果:

复制代码
[[0.54735762 0.57932425 0.61063923]
 [0.57199613 0.61301418 0.65248946]
 [0.5962827  0.64565631 0.6921095 ]
 [0.62010643 0.67699586 0.72908792]]

2. 向量化三层神经网络 my_sequential_v

完整代码

python

运行

复制代码
def my_sequential_v(X, W1, b1, W2, b2, W3, b3):
    """向量化实现三层全连接神经网络(批量前向传播)"""
    A1 = my_dense_v(X, W1, b1, sigmoid)  # 第一层:(1000,400)→(1000,25)
    A2 = my_dense_v(A1, W2, b2, sigmoid)  # 第二层:(1000,25)→(1000,15)
    A3 = my_dense_v(A2, W3, b3, sigmoid)  # 第三层:(1000,15)→(1000,1)
    return A3
批量预测验证

python

运行

复制代码
# 提取权重(和之前一致)
W1_tmp, b1_tmp = layer1.get_weights()
W2_tmp, b2_tmp = layer2.get_weights()
W3_tmp, b3_tmp = layer3.get_weights()

# 一次性对所有1000个样本预测
Prediction = my_sequential_v(X, W1_tmp, b1_tmp, W2_tmp, b2_tmp, W3_tmp, b3_tmp)
print(Prediction.shape)  # 输出:(1000, 1)

# 批量转换为标签(0/1)
Yhat = (Prediction >= 0.5).astype(int)
print("predict a zero:", Yhat[0], "predict a one:", Yhat[500])
# 输出:predict a zero: [0] predict a one: [1]

3. 向量化 vs 循环实现对比

表格

对比维度 my_dense(循环实现) my_dense_v(向量化实现)
输入支持 单样本 (n,) 批量样本 (m, n)
计算方式 for 循环逐个单元计算 矩阵乘法一次性批量计算
效率 慢(循环开销大) 快(利用 NumPy 底层优化)
代码长度 多行循环逻辑 两行核心代码
适用场景 理解底层原理、单样本推理 批量训练、大规模数据处理

📌 作业核心知识点总结

  1. 前向传播底层逻辑:全连接层的核心是「加权和 + 激活函数」,手动实现可以帮你理解 TensorFlow/Keras 封装的底层计算。
  2. 单样本 vs 批量计算:循环实现适合理解原理,向量化矩阵运算适合实际工程场景,大幅提升效率。
  3. 维度匹配原则
    • 权重矩阵 W(输入维度, 输出维度)
    • 偏置向量 b(输出维度,)(单样本)或 (1, 输出维度)(批量)
    • 矩阵乘法 np.matmul(A, W):要求 A 的列数等于 W 的行数
  4. NumPy 广播机制 :偏置向量 b 可以自动适配批量维度,无需手动复制,是向量化实现的关键。

神经元和神经元层 实验课笔记

这是吴恩达深度学习课程中,用 TensorFlow/Keras 理解「神经元」本质的实验笔记,核心是:神经元 = 线性 / 逻辑回归的简化实现,下面我们分模块拆解。


一、核心概念:生物神经元 vs 数学模型

表格

生物神经元 简化数学模型
树突接收输入信号、细胞体处理、轴突输出信号 输入(数值)→ 线性计算(权重 + 偏置)→ 激活函数 → 输出
多个信号输入,整合后输出一个信号 多输入加权求和,再通过激活函数映射

简单说:深度学习里的「神经元」,就是把生物神经元的信号传递过程,转化成了可计算的数学函数。


二、环境准备:TensorFlow & Keras

1. 背景介绍

  • TensorFlow:Google 开发的机器学习框架,2019 年 TensorFlow 2.0 集成了 Keras。
  • Keras:Francois Chollet 开发的高层 API,提供简洁的「以层为中心」接口,是 TensorFlow 中构建模型的常用方式。

2. 导入依赖库

python

运行

复制代码
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras import Sequential
from tensorflow.keras.losses import MeanSquaredError, BinaryCrossentropy
from tensorflow.keras.activations import sigmoid
from lab_utils_common import dlc
from lab_neurons_utils import plt_prob_1d, sigmoidnp, plt_linear, plt_logistic
plt.style.use('./deeplearning.mplstyle')
import logging
tf.autograph.set_verbosity(0)  # 简化日志输出

三、无激活的神经元:线性回归模型

无激活的神经元,本质就是我们熟悉的线性回归,公式为:\(f_{w,b}(x^{(i)}) = \mathbf{w} \cdot x^{(i)} + b\)

1. 数据集准备(房价预测例子)

python

运行

复制代码
# 输入:房屋面积(千平方英尺),输出:房价(千美元)
X_train = np.array([[1.0], [2.0]], dtype=np.float32)
Y_train = np.array([[300.0], [500.0]], dtype=np.float32)

# 可视化数据
fig, ax = plt.subplots(1,1)
ax.scatter(X_train, Y_train, marker='x', c='r', label="Data Points")
ax.legend(fontsize='xx-large')
ax.set_ylabel('Price (in 1000s of dollars)', fontsize='xx-large')
ax.set_xlabel('Size (1000 sqft)', fontsize='xx-large')
plt.show()

2. 用 Keras 构建「线性神经元」

python

运行

复制代码
# 定义一个无激活的Dense层(线性层),units=1 表示输出1个值
linear_layer = tf.keras.layers.Dense(units=1, activation='linear')
关键细节:权重的实例化
  • 刚创建层时,权重并未初始化,linear_layer.get_weights() 返回空列表 []
  • 当第一次传入数据时,框架会自动推断输入维度,并初始化权重(默认随机初始化,偏置初始为 0)。

python

运行

复制代码
# 传入一个样本触发权重初始化
a1 = linear_layer(X_train[0].reshape(1,1))
print(a1)  # 输出:tf.Tensor([[1.37]], shape=(1, 1), dtype=float32)

# 查看初始化后的权重和偏置
w, b = linear_layer.get_weights()
print(f"w = {w}, b={b}")  # 输出:w = [[1.37]], b=[0.]

3. 手动设置权重,验证与线性回归等价

我们手动设置 w=200, b=100,对应模型 y=200x+100,验证和线性回归的结果是否一致。

python

运行

复制代码
# 设置权重和偏置
set_w = np.array([[200]])
set_b = np.array([100])
linear_layer.set_weights([set_w, set_b])
print(linear_layer.get_weights())  # 输出:[array([[200.]], dtype=float32), array([100.], dtype=float32)]

# 对比 Keras 层输出 和 NumPy 线性回归计算结果
a1 = linear_layer(X_train[0].reshape(1,1))
print(a1)  # 输出:tf.Tensor([[300.]], shape=(1, 1), dtype=float32)

alin = np.dot(set_w, X_train[0].reshape(1,1)) + set_b
print(alin)  # 输出:[[300.]]

4. 预测并可视化

python

运行

复制代码
# 用 Keras 层和 NumPy 分别预测所有数据
prediction_tf = linear_layer(X_train)
prediction_np = np.dot(X_train, set_w) + set_b

# 对比可视化,两者完全一致
plt_linear(X_train, Y_train, prediction_tf, prediction_np)

结论:无激活的 Dense 层,和线性回归模型完全等价


四、带 Sigmoid 激活的神经元:逻辑回归模型

带 Sigmoid 激活的神经元,本质就是逻辑回归,公式为:\(f_{w,b}(x^{(i)}) = g(\mathbf{w}x^{(i)} + b), \quad g(x) = \text{sigmoid}(x) = \frac{1}{1+e^{-x}}\)

1. 数据集准备(二分类例子)

python

运行

复制代码
# 输入特征x,输出二分类标签y(0或1)
X_train = np.array([0., 1, 2, 3, 4, 5], dtype=np.float32).reshape(-1,1)
Y_train = np.array([0, 0, 0, 1, 1, 1], dtype=np.float32).reshape(-1,1)

# 分离正负样本,可视化
pos = Y_train == 1
neg = Y_train == 0

fig,ax = plt.subplots(1,1,figsize=(4,3))
ax.scatter(X_train[pos], Y_train[pos], marker='x', s=80, c='red', label="y=1")
ax.scatter(X_train[neg], Y_train[neg], marker='o', s=100, label="y=0", 
           facecolors='none', edgecolors=dlc["d1blue"], lw=3)
ax.set_ylim(-0.08,1.1)
ax.set_ylabel('y', fontsize=12)
ax.set_xlabel('x', fontsize=12)
ax.set_title('one variable plot')
ax.legend(fontsize=12)
plt.show()

2. 用 Keras 构建「逻辑神经元」

python

运行

复制代码
# 用 Sequential 构建一个单神经元模型,带 sigmoid 激活
model = Sequential([
    tf.keras.layers.Dense(1, input_dim=1, activation='sigmoid', name='L1')
])

# 查看模型结构:只有1层,2个参数(w和b)
model.summary()

plaintext

复制代码
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 L1 (Dense)                  (None, 1)                 2         
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0
_________________________________________________________________

3. 手动设置权重,验证与逻辑回归等价

手动设置 w=2, b=-4.5,验证 Keras 层和 NumPy 实现的逻辑回归结果是否一致。

python

运行

复制代码
# 获取层并设置权重
logistic_layer = model.get_layer('L1')
set_w = np.array([[2]])
set_b = np.array([-4.5])
logistic_layer.set_weights([set_w, set_b])
print(logistic_layer.get_weights())  # 输出:[array([[2.]], dtype=float32), array([-4.5], dtype=float32)]

# 对比 Keras 预测 和 NumPy 逻辑回归计算结果
a1 = model.predict(X_train[0].reshape(1,1))
print(a1)  # 输出:[[0.01]]

alog = sigmoidnp(np.dot(set_w, X_train[0].reshape(1,1)) + set_b)
print(alog)  # 输出:[[0.01]]

4. 预测并可视化

python

运行

复制代码
# 可视化 sigmoid 输出,阴影部分表示模型输出的概率值(0到1之间)
plt_logistic(X_train, Y_train, model, set_w, set_b, pos, neg)

结论:带 sigmoid 激活的 Dense 层,和逻辑回归模型完全等价


五、核心总结

  1. 神经元的本质
    • 无激活神经元 = 线性回归:\(y = wx + b\)
    • 带 sigmoid 激活神经元 = 逻辑回归:\(y = \text{sigmoid}(wx + b)\)
  2. Keras Dense 层的理解
    • units=1:输出 1 个值,对应单个神经元。
    • activation='linear':无激活,线性回归。
    • activation='sigmoid':带激活,逻辑回归。
  3. 权重与偏置:每个神经元都有一个权重矩阵 w 和偏置 b,是模型需要学习的参数。

补充:关键代码与易错点

表格

操作 易错点 解决方式
Dense 层输入 输入必须是二维数组((样本数, 特征数) reshape(1,1) 把单个样本转为二维
权重实例化 未传入数据时,get_weights() 返回空 先调用层传入数据,或直接 set_weights()
逻辑回归实现 忘记加 sigmoid 激活 明确 activation='sigmoid'

需要我把这份笔记整理成一份可直接运行的完整代码文件,方便你在本地复现实验吗?

简单神经网络(咖啡烘焙案例)实验笔记

这是吴恩达深度学习课程中,用 TensorFlow/Keras 构建小型二分类神经网络 的实验笔记,核心是理解两层神经网络的完整流程(数据处理→模型构建→训练→预测→可视化),下面分模块拆解。


一、实验背景与目标

1. 问题描述

  • 任务:根据「烘焙温度(℃)」和「烘焙时间(分钟)」两个特征,预测咖啡烘焙是否合格(好 / 坏),属于二分类问题。
  • 背景知识:好的烘焙区间是温度 175-260℃、时间 12-15 分钟,且温度越高,所需时间越短。

2. 神经网络结构

  • 输入层:2 个特征(温度、时间)
  • 隐藏层:3 个神经元,Sigmoid 激活
  • 输出层:1 个神经元,Sigmoid 激活(输出合格概率)

二、环境与数据准备

1. 导入依赖库

python

运行

复制代码
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('./deeplearning.mplstyle')
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from lab_utils_common import dlc
from lab_coffee_utils import load_coffee_data, plt_roast, plt_prob, plt_layer, plt_network, plt_output_unit
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
tf.autograph.set_verbosity(0)

2. 加载并可视化数据集

python

运行

复制代码
# 加载数据
X,Y = load_coffee_data();
print(X.shape, Y.shape)  # 输出:样本数,特征数 和 样本数,标签数

# 可视化烘焙数据
plt_roast(X,Y)
  • 数据分布:红色叉号是合格烘焙,蓝色圆圈是不合格烘焙,合格样本集中在温度 175-240℃、时间 12-14 分钟的区域。

3. 数据规范化(标准化)

神经网络训练前,必须对特征做标准化,让特征分布在相近的区间,加速梯度下降收敛。

python

运行

复制代码
# 查看标准化前的特征范围
print(f"Temperature Max, Min pre normalization: {np.max(X[:,0]):.2f}, {np.min(X[:,0]):.2f}")
print(f"Duration    Max, Min pre normalization: {np.max(X[:,1]):.2f}, {np.min(X[:,1]):.2f}")

# 创建标准化层并拟合数据(学习均值和方差)
norm_l = tf.keras.layers.Normalization(axis=-1)
norm_l.adapt(X)  

# 对数据做标准化处理
Xn = norm_l(X)
print(f"Temperature Max, Min post normalization: {np.max(Xn[:,0]):.2f}, {np.min(Xn[:,0]):.2f}")
print(f"Duration    Max, Min post normalization: {np.max(Xn[:,1]):.2f}, {np.min(Xn[:,1]):.2f}")
  • 标准化后,两个特征的范围从 [151.32,284.99][11.51,15.45] 变为 [-1.69,1.66][-1.70,1.79],分布更接近标准正态分布。

4. 数据扩增(平铺复制)

为了减少训练轮数,将数据复制 1000 倍,扩大训练集规模:

python

运行

复制代码
Xt = np.tile(Xn,(1000,1))
Yt= np.tile(Y,(1000,1))   
print(Xt.shape, Yt.shape)  # 输出:(200000, 2) (200000, 1)

三、构建 TensorFlow 模型

1. 定义 Sequential 模型

python

运行

复制代码
# 设置随机种子,保证结果可复现
tf.random.set_seed(1234)  

model = Sequential(
    [
        tf.keras.Input(shape=(2,)),  # 指定输入特征维度为2
        Dense(3, activation='sigmoid', name = 'layer1'),  # 隐藏层:3个神经元,Sigmoid激活
        Dense(1, activation='sigmoid', name = 'layer2')   # 输出层:1个神经元,Sigmoid激活(二分类)
    ]
)

2. 查看模型结构与参数

python

运行

复制代码
model.summary()

plaintext

复制代码
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 layer1 (Dense)              (None, 3)                 9         
                                                                 
 layer2 (Dense)              (None, 1)                 4         
                                                                 
=================================================================
Total params: 13
Trainable params: 13
Non-trainable params: 0
_________________________________________________________________
  • 参数计算验证:
    • 隐藏层:输入特征数 2 → 3 个神经元,参数数 = 2×3 + 3 = 9(权重 + 偏置)
    • 输出层:输入 3 → 1 个神经元,参数数 = 3×1 + 1 = 4
    • 总参数:9 + 4 = 13

3. 查看初始权重与偏置

python

运行

复制代码
W1, b1 = model.get_layer("layer1").get_weights()
W2, b2 = model.get_layer("layer2").get_weights()
print(f"W1{W1.shape}:\n", W1, f"\nb1{b1.shape}:", b1)
print(f"W2{W2.shape}:\n", W2, f"\nb2{b2.shape}:", b2)
  • 初始权重随机初始化,偏置默认初始化为 0。

四、模型编译与训练

1. 编译模型

python

运行

复制代码
model.compile(
    loss = tf.keras.losses.BinaryCrossentropy(),  # 二分类交叉熵损失
    optimizer = tf.keras.optimizers.Adam(learning_rate=0.01),  # Adam优化器
)

2. 训练模型

python

运行

复制代码
model.fit(
    Xt,Yt,
    epochs=10,  # 训练10轮
)

训练过程中损失值会逐步下降:

plaintext

复制代码
Epoch 1/10
6250/6250 [==============================] - 5s 768us/step - loss: 0.1782
Epoch 2/10
6250/6250 [==============================] - 5s 781us/step - loss: 0.1165
...
Epoch 10/10
6250/6250 [==============================] - 5s 787us/step - loss: 0.0052

3. 训练后更新权重

训练完成后,模型会更新权重和偏置,你也可以手动加载预训练权重,保证结果稳定:

python

运行

复制代码
# 查看训练后的权重
W1, b1 = model.get_layer("layer1").get_weights()
W2, b2 = model.get_layer("layer2").get_weights()

# 手动加载预训练权重(可选,用于保证复现性)
W1 = np.array([
    [-8.94,  0.29, 12.89],
    [-0.17, -7.34, 10.79]] )
b1 = np.array([-9.87, -9.28,  1.01])
W2 = np.array([
    [-31.38],
    [-27.86],
    [-32.79]])
b2 = np.array([15.54])
model.get_layer("layer1").set_weights([W1,b1])
model.get_layer("layer2").set_weights([W2,b2])

五、模型预测与决策

1. 对新数据做预测

python

运行

复制代码
# 定义测试样本:一个合格、一个不合格
X_test = np.array([
    [200,13.9],  # 合格样本
    [200,17]])   # 不合格样本

# 测试数据必须和训练数据一样做标准化
X_testn = norm_l(X_test)

# 预测概率
predictions = model.predict(X_testn)
print("predictions = \n", predictions)

输出结果(概率值):

plaintext

复制代码
predictions = 
 [[9.63e-01]
 [3.03e-08]]

2. 概率转决策(二分类)

设置阈值 0.5,概率≥0.5 判定为合格(1),否则为不合格(0):

python

运行

复制代码
# 方法1:循环实现
yhat = np.zeros_like(predictions)
for i in range(len(predictions)):
    if predictions[i] >= 0.5:
        yhat[i] = 1
    else:
        yhat[i] = 0

# 方法2:向量化实现(更简洁)
yhat = (predictions >= 0.5).astype(int)
print(f"decisions = \n{yhat}")

输出:

plaintext

复制代码
decisions = 
 [[1]
 [0]]

六、模型可视化与理解

1. 隐藏层神经元功能可视化

隐藏层的每个神经元都是一个逻辑回归单元,负责识别不同的 "不合格烘焙区域":

python

运行

复制代码
plt_layer(X,Y.reshape(-1,),W1,b1,norm_l)
  • 单元 0:识别温度过低的不合格样本
  • 单元 1:识别烘焙时间过短的不合格样本
  • 单元 2:识别时间 / 温度组合不佳的不合格样本

2. 输出层可视化

输出层将隐藏层的 3 个输出组合起来,最终判断是否为合格烘焙:

python

运行

复制代码
plt_output_unit(W2,b2)
  • 输出值高的区域对应 "不合格烘焙",输出值低的区域对应 "合格烘焙"。

3. 完整网络决策边界可视化

python

运行

复制代码
netf= lambda x : model.predict(norm_l(x))
plt_network(X,Y,netf)
  • 左图:模型输出的概率分布,蓝色越深代表合格概率越高
  • 右图:模型的最终决策结果,橙色叉号是模型判定为合格的样本,与真实标签高度吻合

七、核心总结

  1. 神经网络结构理解:两层神经网络的本质是「特征提取 + 分类」,隐藏层学习非线性特征,输出层做最终决策。
  2. 数据预处理关键:标准化是神经网络训练的必要步骤,能让梯度下降更稳定、收敛更快。
  3. 模型训练流程:定义模型 → 编译(指定损失和优化器)→ 训练(拟合数据)→ 预测(概率转决策)。
  4. 神经元的功能:每个隐藏层神经元都在学习一个简单的逻辑规则,组合起来就能解决复杂的非线性分类问题。

需要我把这份笔记整理成一份可直接运行的完整代码文件,并补充常见训练问题(如过拟合、不收敛)的解决方法吗?

用 NumPy 从零实现神经网络(咖啡烘焙案例)实验笔记

这是吴恩达深度学习课程中,不依赖 TensorFlow/Keras,用纯 NumPy 实现两层神经网络的实验笔记,核心是理解「前向传播的底层原理」,和之前 TensorFlow 实现的咖啡烘焙模型完全等价。


一、实验目标与背景

  • 目标:用 NumPy 手动实现一个两层神经网络(隐藏层 3 个神经元,输出层 1 个神经元),完成咖啡烘焙合格 / 不合格的二分类任务。
  • 背景:和之前 TensorFlow 实验使用完全相同的数据集和网络结构,帮助理解框架背后的底层数学逻辑。

二、环境与数据准备

1. 导入依赖库

python

运行

复制代码
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('./deeplearning.mplstyle')
import tensorflow as tf
from lab_utils_common import dlc, sigmoid
from lab_coffee_utils import load_coffee_data, plt_roast, plt_prob, plt_layer, plt_network, plt_output_unit
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
tf.autograph.set_verbosity(0)

2. 加载与标准化数据

和 TensorFlow 实验完全一致:

python

运行

复制代码
# 加载数据(200个样本,2个特征:温度、时间)
X,Y = load_coffee_data();
print(X.shape, Y.shape)  # 输出:(200, 2) (200, 1)

# 可视化数据
plt_roast(X,Y)

# 数据标准化(使用和之前相同的 Normalization 层)
print(f"Temperature Max, Min pre normalization: {np.max(X[:,0]):.2f}, {np.min(X[:,0]):.2f}")
print(f"Duration    Max, Min pre normalization: {np.max(X[:,1]):.2f}, {np.min(X[:,1]):.2f}")

norm_l = tf.keras.layers.Normalization(axis=-1)
norm_l.adapt(X)
Xn = norm_l(X)

print(f"Temperature Max, Min post normalization: {np.max(Xn[:,0]):.2f}, {np.min(Xn[:,0]):.2f}")
print(f"Duration    Max, Min post normalization: {np.max(Xn[:,1]):.2f}, {np.min(Xn[:,1]):.2f}")

三、核心:用 NumPy 实现神经网络层

1. 实现单个全连接层 my_dense

这是神经网络的基础单元,对应 Keras 的 Dense 层,手动实现「线性计算 + 激活函数」的过程:

python

运行

复制代码
def my_dense(a_in, W, b, g):
    """
    手动实现全连接层
    Args:
        a_in: 输入数据(单个样本,n_features 维)
        W: 权重矩阵(n_features × n_units)
        b: 偏置向量(n_units 维)
        g: 激活函数(如 sigmoid)
    Returns:
        a_out: 层输出(n_units 维)
    """
    units = W.shape[1]  # 获取该层神经元数量
    a_out = np.zeros(units)  # 初始化输出
    
    # 遍历每个神经元,计算输出
    for j in range(units):
        w = W[:,j]  # 当前神经元的权重向量
        z = np.dot(w, a_in) + b[j]  # 线性计算:z = w·x + b
        a_out[j] = g(z)  # 应用激活函数
        
    return a_out
  • 关键逻辑:每个神经元都执行「输入与权重的点积 + 偏置 → 激活函数」,和数学公式完全对应。

2. 实现两层神经网络 my_sequential

把两个 my_dense 层组合起来,实现完整的前向传播:

python

运行

复制代码
def my_sequential(x, W1, b1, W2, b2):
    """
    两层神经网络前向传播
    Args:
        x: 输入数据(单个样本)
        W1, b1: 隐藏层权重和偏置
        W2, b2: 输出层权重和偏置
    Returns:
        a2: 模型输出(合格概率)
    """
    a1 = my_dense(x, W1, b1, sigmoid)  # 隐藏层(3个神经元,sigmoid激活)
    a2 = my_dense(a1, W2, b2, sigmoid)  # 输出层(1个神经元,sigmoid激活)
    return a2

3. 加载预训练权重

直接使用之前 TensorFlow 实验中训练好的权重,保证模型结果和框架实现完全一致:

python

运行

复制代码
# 预训练权重(来自 TensorFlow 模型)
W1_tmp = np.array([[-8.93, 0.29, 12.9], [-0.1, -7.32, 10.81]])
b1_tmp = np.array([-9.82, -9.28, 0.96])
W2_tmp = np.array([[-31.18], [-27.59], [-32.56]])
b2_tmp = np.array([15.41])

四、实现批量预测函数 my_predict

和 Keras 的 model.predict() 功能等价,支持批量样本输入:

python

运行

复制代码
def my_predict(X, W1, b1, W2, b2):
    """
    批量预测函数
    Args:
        X: 输入数据(m × n_features,m个样本)
        W1, b1, W2, b2: 模型权重和偏置
    Returns:
        p: 每个样本的预测概率(m × 1)
    """
    m = X.shape[0]  # 样本数量
    p = np.zeros((m, 1))  # 初始化预测结果
    
    # 遍历每个样本,调用前向传播
    for i in range(m):
        p[i, 0] = my_sequential(X[i], W1, b1, W2, b2)
        
    return p

五、模型测试与决策

1. 测试样本预测

python

运行

复制代码
# 定义测试样本:一个合格、一个不合格
X_tst = np.array([
    [200,13.9],  # 合格样本(温度200℃,时间13.9分钟)
    [200,17]])   # 不合格样本(温度200℃,时间17分钟)

# 测试数据必须和训练数据一样做标准化
X_tstn = norm_l(X_tst)

# 预测概率
predictions = my_predict(X_tstn, W1_tmp, b1_tmp, W2_tmp, b2_tmp)
print("predictions = \n", predictions)

输出(和 TensorFlow 结果完全一致):

plaintext

复制代码
predictions = 
 [[9.63e-01]
 [3.03e-08]]

2. 概率转决策(二分类)

设置 0.5 为阈值,将概率转为 0/1 决策:

python

运行

复制代码
# 方法1:循环实现
yhat = np.zeros_like(predictions)
for i in range(len(predictions)):
    if predictions[i] >= 0.5:
        yhat[i] = 1
    else:
        yhat[i] = 0

# 方法2:向量化实现(更简洁高效)
yhat = (predictions >= 0.5).astype(int)
print(f"decisions = \n{yhat}")

输出:

plaintext

复制代码
decisions = 
 [[1]
 [0]]

六、模型功能可视化

和 TensorFlow 实验一样,可视化模型的决策边界,验证 NumPy 实现和框架实现的等价性:

python

运行

复制代码
# 定义预测函数,用于可视化
netf = lambda x : my_predict(norm_l(x), W1_tmp, b1_tmp, W2_tmp, b2_tmp)

# 绘制网络概率分布和决策结果
plt_network(X, Y, netf)
  • 左图:模型输出的概率分布,蓝色越深代表合格概率越高,和真实数据的合格区域完全吻合。
  • 右图:模型的最终决策结果,橙色叉号是模型判定为合格的样本,与 TensorFlow 实现的结果完全一致。

七、核心总结

  1. 框架背后的本质 :Keras 的 Dense 层本质上就是「线性计算 + 激活函数」,用 NumPy 手动实现的过程和框架底层逻辑完全相同。
  2. 前向传播的流程:输入数据 → 隐藏层(线性计算 + 激活)→ 输出层(线性计算 + 激活)→ 输出概率。
  3. 数据预处理的一致性:测试数据必须和训练数据使用相同的标准化处理,否则模型预测会出错。
  4. 批量预测的实现 :通过循环遍历每个样本,调用前向传播函数,即可实现和 model.predict() 相同的批量预测功能。
相关推荐
众生皆苦,我是红豆奶茶味1 小时前
【工具】Codex 配置文件速查笔记(截至 2026 年 05 月 09 日)
人工智能·笔记·python·深度学习·神经网络
tiger从容淡定是人生1 小时前
Vibe Coding——中国信创生态真正的“超级加速器”
大数据·人工智能·vibe coding·信创战略
叶沧ii大数据全栈呀1 小时前
【数据智能】从“提需求等排期“到“数据追着人跑“:数据中台到Data Agent的变革复盘
大数据·人工智能·ai编程
2601_957190901 小时前
超元力元宇宙科幻乐园整馆方案——数字科技重塑文旅研学新生态
大数据·人工智能·科技
tanis_20771 小时前
PDF 解析后输出什么格式?MinerU 五类下游场景的选型指南
人工智能·pdf·csdn开发云
美摄科技1 小时前
GAN美颜SDK技术方案,用AI重新定义 “真实”!
人工智能·神经网络·生成对抗网络
互联网推荐官1 小时前
上海软件定制开发技术路径深度拆解:架构选型、工程落地与平台能力实测
人工智能·软件工程
水上冰石1 小时前
2026主流大模型编程工具及对比
人工智能
红色星际1 小时前
东软睿驰以安全开放的软件底座,加速AI Agent规模化上车
人工智能·安全