在这里插入图片描述


1. 课前导读

1.1 本节课学习目标

  • 熟练掌握Python在深度学习中的进阶语法,包括列表推导式、迭代器与生成器、装饰器、上下文管理器。
  • 理解NumPy库的核心数据结构ndarray,掌握数组创建、索引切片、向量化运算、广播机制。
  • 学会使用Matplotlib进行基本的数据可视化,为模型训练过程监控打下基础。
  • 掌握面向对象编程(OOP)在深度学习中的应用:类的定义与继承、@property、静态方法与类方法。
  • 能够使用Pandas进行简单的数据加载与预处理(CSV、缺失值处理)。
  • 综合运用Python、NumPy、OOP思想搭建一个可扩展的数据处理器框架。

1.2 知识重难点

类别 内容
重点 NumPy向量化运算与广播机制;面向对象中的类继承与多态;生成器在大规模数据加载中的优势
难点 装饰器的底层原理(闭包);NumPy广播规则的完全理解;__slots__与内存优化
易混淆点 deepcopyview的区别;Python普通循环与向量化运算的性能差异;实例方法/类方法/静态方法的适用场景

1.3 学习前置条件

  • 已安装Python 3.9及以上版本(第1课已完成TensorFlow安装,Python环境已就绪)。
  • 了解Python基础语法(变量、数据类型、条件判断、循环、函数定义)。
  • 完成第1课的张量基本操作,对本专栏风格已有认知。

1.4 学完可掌握能力

  • 能够编写高效的数据预处理流水线,避免低效Python循环。
  • 使用NumPy完成矩阵运算、统计汇总、形状变换,为TensorFlow张量操作奠定基础。
  • 设计具有复用性的深度学习组件(自定义层、数据加载器)——基于面向对象。
  • 独立读取CSV/Excel格式的数据集并进行清洗、归一化等预处理。
  • 使用Matplotlib绘制损失曲线、特征分布图等常见训练图表。

1.5 行业应用场景

  • 数据预处理:原始日志数据清洗、特征提取、归一化(如用户行为特征工程)。
  • 自定义模型组件:在TensorFlow中通过继承tf.keras.layers.Layer实现非标准算子。
  • 数据增强:图像数据增强(旋转、翻转)的底层实现(配合NumPy)。
  • 训练监控工具:利用装饰器自动记录每个epoch的训练时间或损失值。
  • 配置管理:使用类封装模型超参数,支持序列化与反序列化。

2. 核心理论精讲

2.1 Python进阶语法在深度学习中的价值

深度学习的代码核心是高效的数据处理 + 灵活的模型构建。Python虽然执行速度较慢,但通过向量化(底层C实现)和合理使用高级语法,可以兼顾开发效率和运行性能。

列表推导式:替代map/filter + lambda,更可读。

# 传统循环
squares = []
for i in range(10):
    squares.append(i**2)

# 列表推导式
squares = [i**2 for i in range(10)]

生成器:处理大规模数据集时,避免一次性加载全部数据到内存。深度学习中的数据流水线(如tf.data.Dataset)底层也使用了类似惰性求值的概念。

装饰器:在TensorFlow中随处可见,例如@tf.function将Python函数转为计算图;自定义装饰器可用于日志记录、计时、梯度监控。

上下文管理器:资源自动管理,典型应用是with tf.GradientTape()自动管理梯度计算资源的创建与释放。

2.2 NumPy:深度学习的数据基石

NumPy提供了一个高性能的多维数组对象ndarray,以及丰富的数学函数库。TensorFlow的张量在设计上极大借鉴了NumPy,二者可以无缝转换(tf.convert_to_tensor / .numpy())。

核心概念:向量化。对数组的操作会自动应用到每个元素上,避免Python层级的显式循环,利用BLAS等底层库实现并行加速。

广播机制:当两个数组形状不同时,NumPy会尝试扩展其中一个数组的维度,使其形状兼容后再进行逐元素运算。规则如下:

  • 从尾部维度开始比对。
  • 维度相等或其中一个为1,则兼容;否则抛出错误。
  • 缺失的维度假定为1。

示例:形状(3,4)的数组与形状(4,)的数组相加,后者会广播为(3,4)。这极大简化了特征归一化(减去均值、除以标准差)的代码。

2.3 面向对象核心:设计可复用的深度学习组件

TensorFlow的Keras API是典型的面向对象设计:LayerModelOptimizer等基类,用户通过继承来扩展。理解OOP思想,才能编写自定义层、自定义损失函数、回调函数等。

类继承与多态

  • 基类定义接口(如call方法),子类实现具体逻辑。
  • 在TensorFlow中,tf.keras.Model要求子类实现call()方法。

属性装饰器@property:将方法当作属性调用,用于对属性进行只读访问或延迟计算。例如将学习率设置为属性,并在设置时自动更新优化器内部状态。

类方法与静态方法

  • @classmethod:操作类变量,可用于工厂模式(如从配置字典创建模型)。
  • @staticmethod:工具函数,不依赖实例或类状态。

__slots__:限制实例属性,减少内存占用。在需要创建大量小对象(如批量数据样本)时非常有用。

2.4 数据预处理流程理论

深度学习的数据预处理通常包括:

  1. 加载:从文件(CSV、JSON、图像)读取原始数据。
  2. 清洗:处理缺失值(删除、均值/中位数填充)、异常值剔除。
  3. 编码:类别特征进行One-Hot或Label Encoding;文本特征转为词向量索引。
  4. 缩放:数值特征归一化(Min-Max)或标准化(Z-Score),使不同特征处于同一量级,加速梯度下降收敛。
  5. 划分:训练集、验证集、测试集(通常6:2:2或8:1:1)。
  6. 批量化:将数据切分为小批量,便于随机梯度下降。

其中,NumPy和Pandas承担了大部分工作。Pandas提供的DataFrame结构非常适合表格数据的操作。


3. 环境搭建与工具配置

本课所需库:numpy, matplotlib, pandas。若已安装TensorFlow,则NumPy通常已被依赖安装,但建议单独确认版本。

3.1 安装必要库

# 激活你的Python环境(假设使用第1课的虚拟环境)
# Windows
tf_env\Scripts\activate
# macOS/Linux
source tf_env/bin/activate

# 安装或升级库
pip install --upgrade numpy pandas matplotlib

# 验证安装
python -c "import numpy; print(numpy.__version__)"   # 应输出 1.24+
python -c "import pandas; print(pandas.__version__)" # 应输出 2.0+
python -c "import matplotlib; print(matplotlib.__version__)"

3.2 配置Jupyter Notebook(可选但推荐)

Jupyter Notebook方便分步执行代码和可视化。

pip install jupyter
jupyter notebook

在浏览器中新建一个Notebook,即可逐段运行本课的代码示例。

3.3 IDE设置建议

  • PyCharm:将项目解释器设置为虚拟环境,并启用Scientific Mode,内置变量查看器。
  • VS Code:安装Python和Jupyter扩展,选择相同解释器。

3.4 常见环境问题

问题 原因 解决
numpy 导入失败提示 DLL load failed Python版本不兼容(如3.11+)或缺少VC++运行库 降级到Python 3.9,或安装 numpy‑1.23.5
matplotlib 绘图无法显示中文 默认字体不支持中文 运行以下代码设置中文字体(以黑体为例):
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
Pandas读取大文件内存耗尽 数据集过大 使用 chunksize 参数分块读取,或转换为NumPy数组后再处理

4. 代码实战教学

本节将分模块编写代码,逐行讲解。建议读者在IDE或Jupyter中边写边运行。

4.1 Python进阶语法实战

4.1.1 列表推导式与生成器表达式
# 1. 列表推导式:生成0-9的平方
squares = [x**2 for x in range(10)]
print("平方列表:", squares)   # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件的列表推导式:提取偶数平方
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print("偶数平方:", even_squares)  # [0, 4, 16, 36, 64]

# 2. 生成器表达式:圆括号代替方括号,惰性求值
gen = (x**2 for x in range(10))
print("生成器类型:", type(gen))   # <class 'generator'>
print("逐个取值:", next(gen), next(gen), next(gen))  # 0 1 4

# 应用场景:大数据集求和(避免创建完整列表)
large_sum = sum(x**2 for x in range(10000000))  # 几乎不占用额外内存
print("和大数:", large_sum)
4.1.2 装饰器:训练计时器
import time

# 定义一个计时装饰器
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 执行耗时: {end - start:.6f} 秒")
        return result
    return wrapper

@timer
def slow_square(n):
    total = 0
    for i in range(n):
        total += i ** 2
    return total

result = slow_square(1000000)
print(f"结果: {result}")

# 在深度学习中,可以用装饰器自动记录每个epoch的时间
4.1.3 上下文管理器:自动文件关闭
# 常规方式:手动关闭
f = open('test.txt', 'w')
f.write('Hello TensorFlow')
f.close()

# 上下文管理器方式:自动关闭
with open('test.txt', 'w') as f:
    f.write('Hello TensorFlow')
# 退出with块时,文件自动关闭

# 自定义上下文管理器(用于资源管理)
class ManagedTimer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.perf_counter()
        print(f"代码块耗时: {self.end - self.start:.6f} 秒")

with ManagedTimer() as timer:
    sum(x**2 for x in range(1000000))
# 自动打印耗时

4.2 NumPy核心操作实战

4.2.1 创建数组与基本属性
import numpy as np

# 从列表创建
arr1 = np.array([1, 2, 3, 4])
print(arr1, arr1.shape, arr1.dtype)   # [1 2 3 4] (4,) int64

# 特殊数组
zeros = np.zeros((3, 4))          # 3x4全0
ones = np.ones((2, 3))            # 2x3全1
eye = np.eye(5)                   # 5x5单位矩阵
random_arr = np.random.rand(2, 3) # 均匀分布[0,1)

# 等差数列
lin = np.linspace(0, 10, 5)       # [ 0.   2.5  5.   7.5 10. ]
print(lin)

# 查看属性
print("维度数:", arr1.ndim)       # 1
print("元素总数:", arr1.size)    # 4
print("每个元素字节:", arr1.itemsize)  # 8 (int64)
4.2.2 索引与切片(重要)
arr = np.arange(12).reshape(3, 4)   # 3行4列
print("原始数组:\n", arr)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# 基本切片: [start:stop:step, start:stop:step]
print("第1行:", arr[0, :])          # [0 1 2 3]
print("第2列:", arr[:, 1])          # [1 5 9]
print("子矩阵(前2行, 1~3列):\n", arr[:2, 1:4])
# [[1 2 3]
#  [5 6 7]]

# 整数数组索引(花式索引)
rows = np.array([0, 2])
cols = np.array([1, 3])
print("取(0,1)和(2,3)元素:", arr[rows, cols])  # [1, 11]

# 布尔索引(筛选大于5的元素)
mask = arr > 5
print("布尔掩码:\n", mask)
print("大于5的元素:", arr[mask])    # [ 6  7  8  9 10 11]

# 注意:花式索引返回的是副本,不是视图;基本切片返回视图(共享内存)
sub_view = arr[0:2, 0:2]
sub_view[0,0] = 100
print("修改视图后原数组受影响:\n", arr[0,0])  # 100
4.2.3 向量化运算与广播
# 逐元素运算
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
print("加法:\n", a + b)
print("乘法:\n", a * b)          # 不是矩阵乘法,是Hadamard积
print("矩阵乘法:\n", a @ b)      # 等价于 np.dot(a,b)

# 通用函数(ufunc)
print("开平方:\n", np.sqrt(a))
print("指数:\n", np.exp(a))

# 广播示例
c = np.array([10, 20])           # shape (2,)
print("广播加法(3x2 + 2):\n", a + c)  
# a形状(2,2)与(2,)相加,(2,)扩展为(2,2) -> [[1+10,2+20],[3+10,4+20]] = [[11,22],[13,24]]

d = np.array([[10], [20]])       # shape (2,1)
print("广播加法(2x2 + 2x1):\n", a + d)
# (2,2)与(2,1)广播,列维度扩展 -> [[1+10,2+10],[3+20,4+20]] = [[11,12],[23,24]]

# 减均值除以标准差(标准化)的一行代码
data = np.random.rand(100, 10)
normalized = (data - data.mean(axis=0)) / data.std(axis=0)
print("标准化后均值接近0:", normalized.mean(axis=0).round(6))
4.2.4 形状变换与聚合
arr = np.arange(24)
print("原始形状:", arr.shape)     # (24,)

# reshape
arr_3d = arr.reshape(2, 3, 4)
print("3D形状:", arr_3d.shape)    # (2,3,4)

# 展平(flatten  vs  ravel)
flat = arr_3d.flatten()           # 返回副本
ravel = arr_3d.ravel()            # 返回视图(尽可能)
print("展平后长度:", flat.size)

# 聚合函数
print("总和:", arr.sum())
print("行方向均值:", arr_3d.mean(axis=1))  # 对中间维度求平均,输出(2,4)
print("最大值的索引:", arr.argmax())

4.3 Pandas数据加载与预处理

import pandas as pd

# 创建一个模拟数据集
data = {
    'feature1': [1.2, 2.3, 3.1, np.nan, 5.0],
    'feature2': [10, 20, 30, 40, 50],
    'label': [0, 1, 0, 1, 1]
}
df = pd.DataFrame(data)
print("原始DataFrame:\n", df)

# 1. 处理缺失值
print("缺失值统计:\n", df.isnull().sum())
df['feature1'].fillna(df['feature1'].mean(), inplace=True)  # 均值填充
print("填充后:\n", df)

# 2. 特征缩放(Min-Max归一化)
from sklearn.preprocessing import MinMaxScaler  # 也可手写
scaler = MinMaxScaler()
df[['feature1', 'feature2']] = scaler.fit_transform(df[['feature1', 'feature2']])
print("归一化后:\n", df)

# 3. 划分特征与标签
X = df[['feature1', 'feature2']].values  # 转为numpy数组
y = df['label'].values
print("X形状:", X.shape, " y:", y)

4.4 面向对象实战:自定义数据集类

import numpy as np

class SimpleDataset:
    """自定义数据集类,模拟TensorFlow的Dataset接口"""
    def __init__(self, X, y, batch_size=32, shuffle=True):
        self.X = np.asarray(X)
        self.y = np.asarray(y)
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.num_samples = len(self.X)
        self.indices = np.arange(self.num_samples)
        if self.shuffle:
            np.random.shuffle(self.indices)
        self.current_idx = 0

    def __iter__(self):
        """使类实例可迭代,用于for循环"""
        self.current_idx = 0
        if self.shuffle:
            np.random.shuffle(self.indices)
        return self

    def __next__(self):
        """每次迭代返回一个batch"""
        if self.current_idx >= self.num_samples:
            raise StopIteration
        start = self.current_idx
        end = min(self.current_idx + self.batch_size, self.num_samples)
        batch_indices = self.indices[start:end]
        X_batch = self.X[batch_indices]
        y_batch = self.y[batch_indices]
        self.current_idx = end
        return X_batch, y_batch

    def __len__(self):
        """返回batch数量"""
        return (self.num_samples + self.batch_size - 1) // self.batch_size

# 测试数据集
X_demo = np.random.rand(100, 5)
y_demo = np.random.randint(0, 2, size=100)
dataset = SimpleDataset(X_demo, y_demo, batch_size=16, shuffle=True)

for epoch in range(2):
    print(f"Epoch {epoch+1}")
    for batch_x, batch_y in dataset:
        print(f"batch shape: {batch_x.shape}, labels: {batch_y.shape}")
    # 每个epoch结束会自动重新shuffle(因为__iter__中重排)

4.5 Matplotlib可视化基础

import matplotlib.pyplot as plt

# 创建模拟损失曲线
epochs = np.arange(1, 101)
loss = 1.0 / (epochs + 10) + np.random.normal(0, 0.01, size=100)
plt.figure(figsize=(10, 5))

plt.subplot(1, 2, 1)
plt.plot(epochs, loss, 'b-', label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.legend()
plt.grid(True)

# 散点图:特征与标签的关系
x_data = np.random.randn(200)
y_data = 2 * x_data + 1 + np.random.randn(200) * 0.3
plt.subplot(1, 2, 2)
plt.scatter(x_data, y_data, alpha=0.5, c='red', edgecolors='k')
plt.xlabel('Feature X')
plt.ylabel('Target Y')
plt.title('Scatter with Noise')

plt.tight_layout()
plt.show()

5. 案例实操演练

案例:从CSV文件中加载房价数据,完成预处理并构建简单的线性回归模型(使用NumPy手动实现梯度下降)

本案例整合Pandas数据加载、NumPy向量化运算、自定义训练循环,不使用TensorFlow高层API,加深对底层原理的理解。

5.1 生成模拟房价数据集

import numpy as np
import pandas as pd

# 生成1000条数据,特征:面积(平米)、卧室数量、房龄(年)
np.random.seed(42)
n_samples = 1000
area = np.random.normal(100, 30, n_samples)          # 面积 100±30
bedrooms = np.random.randint(1, 5, n_samples)        # 卧室 1~4
age = np.random.uniform(0, 50, n_samples)            # 房龄 0~50

# 真实权重: price = 5000*area + 20000*bedrooms - 1000*age + 50000 + noise
true_weights = np.array([5000, 20000, -1000])
bias = 50000
noise = np.random.normal(0, 20000, n_samples)
price = (area * true_weights[0] + bedrooms * true_weights[1] + age * true_weights[2] + bias + noise).astype(np.float32)

# 构建DataFrame并保存为CSV
df = pd.DataFrame({'area': area, 'bedrooms': bedrooms, 'age': age, 'price': price})
df.to_csv('house_prices.csv', index=False)
print("数据样例:\n", df.head())

5.2 使用Pandas加载与预处理

# 加载数据
df = pd.read_csv('house_prices.csv')
print("数据形状:", df.shape)
print("缺失值:\n", df.isnull().sum())

# 提取特征和标签
X_raw = df[['area', 'bedrooms', 'age']].values.astype(np.float32)
y_raw = df['price'].values.astype(np.float32).reshape(-1, 1)

# 标准化特征 (Z-score)
mean = X_raw.mean(axis=0)
std = X_raw.std(axis=0)
X = (X_raw - mean) / std

# 添加一列1作为偏置的系数(将偏置纳入权重矩阵)
X_with_bias = np.c_[X, np.ones((n_samples, 1))]   # shape (1000, 4)

# 分割训练集和测试集 (80% train, 20% test)
split = int(0.8 * n_samples)
X_train, X_test = X_with_bias[:split], X_with_bias[split:]
y_train, y_test = y_raw[:split], y_raw[split:]

print("训练集形状:", X_train.shape, y_train.shape)

5.3 NumPy实现批量梯度下降

# 初始化参数 w (4个权重,最后一个对应偏置)
w = np.random.randn(4, 1).astype(np.float32) * 0.01

learning_rate = 0.01
epochs = 500
batch_size = 64
loss_history = []

# 训练
for epoch in range(epochs):
    # 随机打乱数据(简单起见,按顺序分批)
    indices = np.random.permutation(len(X_train))
    X_shuffled = X_train[indices]
    y_shuffled = y_train[indices]

    epoch_loss = 0.0
    for i in range(0, len(X_train), batch_size):
        X_batch = X_shuffled[i:i+batch_size]
        y_batch = y_shuffled[i:i+batch_size]

        # 前向传播
        y_pred = X_batch @ w
        loss = np.mean((y_pred - y_batch) ** 2) / 2   # MSE/2

        # 反向传播:梯度 dW = X_batch.T @ (y_pred - y_batch) / batch_size
        grad = (X_batch.T @ (y_pred - y_batch)) / len(X_batch)
        # 更新参数
        w -= learning_rate * grad

        epoch_loss += loss * len(X_batch)

    epoch_loss /= len(X_train)
    loss_history.append(epoch_loss)
    if (epoch+1) % 100 == 0:
        print(f"Epoch {epoch+1}, Loss: {epoch_loss:.2f}")

# 测试集评估
y_pred_test = X_test @ w
test_loss = np.mean((y_pred_test - y_test) ** 2) / 2
print(f"测试集MSE/2: {test_loss:.2f}")
print(f"学习到的权重: {w.flatten()}")  # 接近 [5000, 20000, -1000, 50000] 但经过标准化,需要还原

5.4 结果可视化

import matplotlib.pyplot as plt

# 绘制损失下降曲线
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss (MSE/2)')
plt.title('Training Loss')
plt.yscale('log')
plt.grid(True)
plt.show()

# 绘制预测值与真实值的散点图
plt.scatter(y_test, y_pred_test, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('True Price')
plt.ylabel('Predicted Price')
plt.title('Prediction vs True')
plt.grid(True)
plt.show()

6. 常见坑点与排错总结

6.1 NumPy坑点

  • 坑1np.array 不指定 dtype 时,整数列表默认为 int64,可能导致后续浮点运算结果截断。
    解决:创建时加上 dtype=np.float32 或使用 astype 转换。

  • 坑2:花式索引与切片混淆,误以为花式索引返回视图,修改后影响原数组。
    解决:花式索引始终返回副本,如需视图,使用 np.take 并设置 out 参数较为复杂;设计时避免对该结果进行写操作。

  • 坑3:广播维度不匹配的错误信息不易理解。
    解决:检查两个数组的 shape,手动模拟广播:从尾部对齐,缺失维度补1,任一轴长度不等于1且不相等则报错。

6.2 Pandas坑点

  • 坑4pd.read_csv 读取大文件时内存不足。
    解决:使用 chunksize 参数分块读取,例如 for chunk in pd.read_csv('large.csv', chunksize=10000): process(chunk)

  • 坑5df.apply 使用自定义Python函数导致极慢。
    解决:优先使用向量化操作(df['col'] * 2);若必须逐行,考虑 swifter 库或转换为NumPy数组。

6.3 面向对象坑点

  • 坑6:在 __init__ 中定义可变默认参数(如 def __init__(self, data=[]))导致实例间共享。
    解决:使用 None 作为默认值,内部再创建新列表。

  • 坑7:子类重写 __init__ 时忘记调用 super().__init__(),导致父类属性未初始化。
    解决:显式调用 super().__init__(*args, **kwargs)

6.4 性能误区

  • 误区1:在循环内重复调用 np.appendnp.concatenate 导致大量内存拷贝。
    正确做法:先预分配列表收集结果,最后一次性转换为数组。

  • 误区2:对所有数据都使用 pandas 而非 numpy,导致不必要的开销。
    正确做法:表格型数据且需复杂索引时用Pandas;纯数值矩阵运算用NumPy。


7. 知识点总结 + 课后作业

7.1 核心知识点梳理

  • Python进阶:列表推导式(简洁高效)、生成器(惰性求值节省内存)、装饰器(横切关注点)、上下文管理器(资源自动管理)。
  • NumPyndarray 多维数组、向量化运算比Python循环快1~2个数量级、广播规则、索引切片(基本切片是视图,花式索引是副本)、通用函数(ufunc)。
  • PandasDataFrame 表结构、缺失值处理、特征缩放、与NumPy无缝转换。
  • 面向对象:类继承与多态、@property、类方法/静态方法、__iter____next__ 实现可迭代数据集类。
  • 可视化:Matplotlib 折线图、散点图的基本绘制。

7.2 基础作业

  1. 使用列表推导式生成一个包含1~100中所有能被3或5整除的数的列表。
  2. 创建一个5x5的随机整数矩阵(范围0~100),输出其最大值、最小值及所有元素的和。
  3. 编写一个装饰器 @repeat(n),使得被装饰函数自动调用n次,并返回结果列表。

7.3 进阶实操作业

任务:自定义标准化类

实现一个StandardScaler类,包含以下方法:

  • fit(X):计算X的均值和方法(要求X为NumPy二维数组)。
  • transform(X):根据fit计算的参数标准化X。
  • fit_transform(X):同时拟合和变换。
  • 支持inverse_transform将标准化后的数据还原。

要求:

  • 使用面向对象,避免冗余代码。
  • 添加属性 mean_std_,如果是fit后调用则存在,否则抛出NotFittedError
  • 对测试集使用训练集的均值和标准差进行标准化(模拟真实场景)。
  • 对比手动实现与sklearn.preprocessing.StandardScaler结果是否一致。

7.4 思考拓展题

  1. 生成器相比列表,在训练深度学习模型时具体有哪些优势?请结合tf.data.Dataset.from_generator说明。
  2. NumPy的广播机制如果使用不当,可能会意外创建巨大的中间数组(如形状(10000,10000)),导致内存爆炸。如何通过np.einsum或显式循环避免?
  3. 在自定义数据集类中,如果希望支持shuffle=True且每个epoch的shuffle不同,但又要保证多次迭代时数据可复现(通过设置随机种子),应该如何设计 __iter__ 方法?

下一课预告:Anaconda开发环境搭建——我们将学习如何使用Conda管理多个Python环境、配置国内镜像源加速下载、统一管理项目依赖,为后续复杂项目的环境隔离打下坚实基础。同时,也会讲解如何在不同的环境中安装TensorFlow GPU版本,避免依赖冲突。


🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航

去订阅

第一部分:基础入门(1-10 课)
第二部分:神经网络核心(11-25 课)
第三部分:进阶网络与框架高阶(26-40 课)
第四部分:企业实战与项目落地(41-50 课)

🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

更多推荐