Skip to content

使用 Python 进行 AI – 深度学习

人工神经网络(Artificial Neural Networks, ANNs)是一种计算系统,其灵感来源于人脑生物神经网络的结构和功能。它们构成了深度学习(Deep Learning, DL)的骨干,深度学习是机器学习(Machine Learning, ML)的一个强大子领域。本章将探索深度学习的基础知识及其在 Python 中的实现。

由于计算能力(特别是 GPU)的进步、大型数据集的可用性以及算法上的突破,深度学习已获得了显著地位。它擅长从数据中学习复杂的模式,使其适用于图像识别、自然语言处理等复杂任务。

深度学习是一种特殊的机器学习形式。以下是突出关键差异的比较:

深度学习算法通常需要大量数据才能显著提升性能。传统的机器学习算法在小数据集上也能工作良好,但随着数据量增加,其性能通常会趋于平稳,这与可以持续改进的深度学习模型不同。

深度学习模型由于层数和参数众多,通常需要高端硬件,特别是 GPU(Graphics Processing Units,图形处理单元)或 TPU(Tensor Processing Units,张量处理单元),以进行高效训练。标准机器学习算法通常可以在 CPU 上有效运行。

在传统的机器学习中,特征提取(选择原始数据并将其转换为适合模型的特征)通常是一个人工的、由领域专家驱动的过程。另一方面,深度学习算法可以直接从数据中自动学习层次化的特征。较低层可能学习简单的特征(例如,图像中的边缘),而较深层则学习更复杂、更抽象的特征(例如,对象的部分)。

深度学习模型的训练可能非常耗时,有时需要数天甚至数周,这取决于大型数据集和复杂的架构。然而,一旦训练完成,推理(对新数据进行预测)可以相对快速。传统的机器学习算法训练时间通常更快,但对于某些复杂的模型来说,推理可能更慢。

深度学习通常采用端到端(end-to-end)的方法,直接从原始输入学习到输出。传统机器学习可能将问题分解为更小的中间步骤,为每个步骤使用单独的模型或过程。

卷积神经网络(CNNs 或 ConvNets)是一类深度神经网络,特别擅长分析视觉图像。虽然标准的神经网络将输入视为一个扁平的向量,但 CNN 被设计用于处理网格状拓扑结构的数据,例如图像(一个像素的 2D 网格)。

CNNs 自动且自适应地学习特征的空间层次结构。初始层学习检测简单的特征,如边缘和角点;后续层将这些特征组合起来检测更复杂的特征,如形状和纹理;更深层可以识别对象部分甚至整个对象。

与层之间完全连接的标准神经网络不同,CNN 使用特定的层类型来利用图像的空间结构。CNN 层中的神经元通常排列在 3 个维度上:宽度、高度和深度(这里的深度指通道数或特征图数)。

  • 输入层(Input Layer):保存图像的原始像素值(例如,高度 x 宽度 x 通道数,其中通道数对于 RGB 为 3,对于灰度为 1)。
  • 卷积层(Convolutional Layer, Conv):核心构建块。它对输入应用一组可学习的滤波器(kernels)。每个滤波器在输入上滑动(卷积),计算点积以创建特征图(feature map)。此过程检测特定模式,如边缘、纹理等。关键参数包括滤波器尺寸(filter size)、步长(stride)和填充(padding)。
  • 激活层(Activation Layer,例如 ReLU):引入非线性到模型中,使其能够学习更复杂的函数。修正线性单元(Rectified Linear Unit, ReLU)(f(x) = max(0,x)) 是一个常见的选择。
  • 池化层(Pooling Layer,例如 MaxPooling):减少特征图的空间维度(宽度、高度),使表示对对象位置的变化更具鲁棒性,并减少计算负载。MaxPooling 从特征图的一小块区域中选择最大值。
  • 全连接层(Fully Connected Layer, Dense):通常位于网络的末端。全连接层中的神经元与前一层中的所有激活都有连接,类似于标准神经网络。这些层执行高级推理并产生最终的分类分数或回归值。
  • 输出层(Output Layer):产生最终输出。对于分类任务,它通常使用 Softmax 激活函数来输出每个类别的概率。

对于 Python 中的深度学习,流行的框架包括 TensorFlow 和 PyTorch。Keras 是一个高级 API,可以在 TensorFlow 之上运行(以前也可以在其他后端之上运行)。我们将重点关注 tensorflow.keras。

您可以使用 pip 安装 TensorFlow(其中包含 Keras):

pip install tensorflow

对于 GPU 支持(对于重要的深度学习任务强烈推荐),您需要兼容的 NVIDIA 驱动程序、CUDA Toolkit 和 cuDNN。TensorFlow 的安装指南提供了详细说明:tensorflow.org/install。

神经网络也可以用于回归任务。这里是如何使用 tensorflow.keras 在波士顿房价数据集(一个经典的包含 13 个数值特征的数据集)上构建一个简单的线性回归器(linear regressor)。

导入必要的包:

import numpy as np
import pandas as pd
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.wrappers.scikit_learn import KerasRegressor # 兼容性封装器
from sklearn.model_selection import cross_val_score, KFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 加载波士顿房价数据集(已从 sklearn 1.2 移除,请使用其他来源)
# 对于此示例,我们假设数据位于 CSV 文件 'boston_housing.csv' 中
# 您可以在 Kaggle 或其他公共数据集仓库中找到此数据集。

加载并准备数据集。(确保 boston_housing.csv 在您的工作目录中或提供正确的路径。CSV 文件应包含 13 个特征列和 1 个目标列 (MEDV)。)

# 加载数据集(替换为实际路径或加载机制)
# 示例:假设 CSV 文件中特征和目标是列
# 确保您的 CSV 文件没有标题行,或者如果有则跳过它。
# 例如:dataframe = pd.read_csv('boston_housing.csv', delim_whitespace=True, header=None)
# 这个数据集有 506 个样本,13 个特征,1 个目标。
# 为了重现性,如果文件不存在,我们将模拟加载它:
# 如果您没有文件,创建一个模拟 CSV 用于演示
# 这不是真实的波士顿房价数据,仅用于代码流程。
import os
if not os.path.exists('boston_housing.csv'):
dummy_data = np.random.rand(506, 14)
pd.DataFrame(dummy_data).to_csv('boston_housing.csv', header=False, index=False)
dataframe = pd.read_csv('boston_housing.csv', header=None)
dataset = dataframe.values
X = dataset[:, 0:13].astype(float)
Y = dataset[:, 13].astype(float)

使用函数定义 Keras 模型(KerasRegressor 封装器需要):

def baseline_model():
model = Sequential()
# 输入层:13 个输入 -> 包含 13 个神经元的全连接层,ReLU 激活
# kernel_initializer='normal' 在旧示例中使用,'glorot_uniform'(默认)或 'he_normal' 常用于 ReLU。
model.add(Dense(13, input_shape=(13,), kernel_initializer='normal', activation='relu'))
# 输出层:1 个神经元(用于回归),没有激活函数以实现线性输出
model.add(Dense(1, kernel_initializer='normal'))
# 编译模型:使用 'adam' 优化器,回归任务使用 'mean_squared_error' 损失函数
model.compile(loss='mean_squared_error', optimizer='adam')
return model

设置随机种子以保证重现性,并使用 K-Fold 交叉验证(cross-validation)评估模型。我们还将对数据进行标准化(standardization)。

seed = 7
np.random.seed(seed)
# 创建一个管道,先标准化数据,然后构建 Keras 模型
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasRegressor(build_fn=baseline_model, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
# K-Fold 交叉验证
kfold = KFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, Y, cv=kfold, scoring='neg_mean_squared_error')
print(f"标准化结果: 平均 MSE: {-results.mean():.2f} (标准差: {results.std():.2f})")

输出将显示均方误差(Mean Squared Error, MSE)。MSE 越低越好。注意:cross_val_score 返回负 MSE 用于最小化,因此我们对其取反。第一个层的 Dense 中的 input_dim 现在是 input_shape。

图像分类器:一个 CNN 应用(猫 vs 狗)

Section titled “图像分类器:一个 CNN 应用(猫 vs 狗)”

让我们构建一个 CNN 来对猫和狗的图像进行分类。我们将使用 tensorflow.keras。您需要一个包含猫和狗图像的数据集,通常组织成 train/cats、train/dogs、validation/cats、validation/dogs 目录。一个受欢迎的来源是 Kaggle 的“Dogs vs Cats”竞赛:kaggle.com/c/dogs-vs-cats/data。在本例中,假设您有一个名为 dataset_cats_dogs 的目录,其结构如下。

导入 Keras 层和用于数据增强(data augmentation)的 ImageDataGenerator:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt # 用于绘制训练历史

定义 CNN 模型架构:

img_width, img_height = 128, 128 # 目标图像尺寸
input_shape_val = (img_width, img_height, 3) # 3 表示 RGB 通道
classifier = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=input_shape_val),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(128, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(512, activation='relu'),
Dropout(0.5), # 用于正则化的 Dropout 层
Dense(1, activation='sigmoid') # 用于二分类(猫或狗)的 Sigmoid 激活函数
])
# 编译模型
classifier.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])

使用 ImageDataGenerator 准备数据。这处理图像加载、调整大小、批量处理和数据增强:

batch_size_val = 32
# 数据集路径(根据需要调整)
# 假设:'dataset_cats_dogs/train' 和 'dataset_cats_dogs/validation'
# 每个包含 'cats' 和 'dogs' 子目录。
# 如果您没有数据集,创建模拟目录和图像以使代码流程运行
if not os.path.exists('dataset_cats_dogs/train/cats'):
os.makedirs('dataset_cats_dogs/train/cats', exist_ok=True)
os.makedirs('dataset_cats_dogs/train/dogs', exist_ok=True)
os.makedirs('dataset_cats_dogs/validation/cats', exist_ok=True)
os.makedirs('dataset_cats_dogs/validation/dogs', exist_ok=True)
# 创建一些模拟图像(例如,复制任何小的 jpg 文件)
# from shutil import copyfile
# for i in range(5): copyfile('any_image.jpg', f'dataset_cats_dogs/train/cats/cat{i}.jpg')
# ...其他文件夹也类似。
# 如果缺少真实数据,这部分对于代码运行至关重要。
print("警告:已创建模拟数据集结构。请使用真实数据进行实际训练。")
train_datagen = ImageDataGenerator(
rescale=1./255, # 将像素值归一化到 [0,1]
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
validation_datagen = ImageDataGenerator(rescale=1./255) # 只对验证集进行缩放
training_set = train_datagen.flow_from_directory(
'dataset_cats_dogs/train', # 训练图像路径
target_size=(img_width, img_height),
batch_size=batch_size_val,
class_mode='binary') # 'binary' 表示 2 个类别
validation_set = validation_datagen.flow_from_directory(
'dataset_cats_dogs/validation', # 验证图像路径
target_size=(img_width, img_height),
batch_size=batch_size_val,
class_mode='binary')

训练模型。使用 model.fit() 代替已弃用的 fit_generator。

epochs_val = 10 # 减少以用于快速演示;要获得良好结果可能需要 25-50 个 epoch
# 确保 steps_per_epoch 和 validation_steps 对于您数据集的大小是合理的
# 如果数据集很小而这些值很大,可能会导致问题。
# 通常:num_train_samples // batch_size 和 num_val_samples // batch_size
steps_per_epoch_val = training_set.samples // batch_size_val
validation_steps_val = validation_set.samples // batch_size_val
if steps_per_epoch_val == 0: steps_per_epoch_val = 1 # 如果数据集太小,避免除以零
if validation_steps_val == 0: validation_steps_val = 1
history = classifier.fit(
training_set,
steps_per_epoch=steps_per_epoch_val,
epochs=epochs_val,
validation_data=validation_set,
validation_steps=validation_steps_val)
# 保存模型:
# classifier.save('cats_vs_dogs_cnn.h5')

训练完成后,您可以对新图像进行预测。(为了简洁起见,省略了预测和绘制历史的代码,但会涉及加载图像,以类似于训练数据的方式进行预处理,并使用 classifier.predict()。)

为了进一步学习,可以探索迁移学习(transfer learning)(使用预训练模型如 VGG16、ResNet 或 MobileNet)、更高级的优化器以及不同的正则化技术等概念。官方的 TensorFlow 和 Keras 文档是极好的资源:tensorflow.org/tutorials。