最近在做一个挺有意思的计算机视觉项目 - 用TensorFlow搭建一个鞋类识别系统。这个项目的主要目标是区分Adidas和Nike两个品牌的鞋子图片。作为一个刚入门深度学习不久的开发者,我觉得这个项目特别适合练手,既能学习卷积神经网络(CNN)的基本原理,又能掌握TensorFlow的实际应用。
项目使用的数据集包含两类图片:Adidas和Nike,每类大约280张图片。虽然数据量不算特别大,但对于学习目的来说已经足够了。整个项目从数据准备到模型训练再到评估,完整地走了一遍深度学习项目的标准流程。
在开始之前,我先搭建好了开发环境:
如果你有NVIDIA显卡,强烈建议配置GPU环境。TensorFlow对GPU的支持很好,能大幅提升训练速度。我的配置代码是这样的:
python复制gpus = tf.config.list_physical_devices("GPU")
if gpus:
gpu0 = gpus[0]
tf.config.experimental.set_memory_growth(gpu0, True)
tf.config.set_visible_devices([gpu0],"GPU")
这段代码做了两件事:
注意:Windows用户可能会遇到CUDA驱动兼容性问题,建议使用WSL2或者Linux系统进行开发。
除了TensorFlow,还需要安装一些辅助库:
bash复制pip install matplotlib pillow numpy
这些库分别用于:
我的数据集目录结构是这样的:
code复制./Data/46-data/
├── train/
│ ├── adidas/
│ └── nike/
└── test/
├── adidas/
└── nike/
使用pathlib库可以很方便地统计图片数量:
python复制data_dir = pathlib.Path("./Data/46-data/")
image_count = len(list(data_dir.glob('*/*/*.jpg')))
print("图片总数为:", image_count) # 输出578
TensorFlow提供了方便的image_dataset_from_directory方法来自动加载图片数据集:
python复制batch_size = 32
img_height = 224
img_width = 224
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
"./Data/46-data/train/",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size
)
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
"./Data/46-data/test/",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size
)
这里有几个关键参数:
image_size:统一调整图片大小为224x224,这是很多预训练模型的输入尺寸batch_size:设为32,这是一个比较通用的值seed:固定随机种子保证可复现性检查数据加载是否正确很重要,我写了个简单的可视化代码:
python复制plt.figure(figsize=(20, 10))
class_names = train_ds.class_names
for images, labels in train_ds.take(1):
for i in range(20):
ax = plt.subplot(5, 10, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
这段代码会显示训练集中的前20张图片及其标签,确保数据加载和标注都正确。
在构建模型前,先优化数据加载管道:
python复制AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
这些操作能显著提升训练效率:
cache():将数据集缓存到内存中shuffle(1000):打乱数据顺序prefetch():预取数据,减少I/O等待时间我设计了一个三层的卷积神经网络:
python复制model = models.Sequential([
layers.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
layers.Conv2D(16, (3, 3), activation='relu'),
layers.AveragePooling2D((2, 2)),
layers.Conv2D(32, (3, 3), activation='relu'),
layers.AveragePooling2D((2, 2)),
layers.Dropout(0.3),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.Dropout(0.3),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(len(class_names))
])
模型结构解析:
经验分享:对于这种二分类问题,网络结构不需要太复杂。我尝试过更深的网络,但效果提升不明显,反而增加了训练时间。
我使用了动态调整的学习率策略:
python复制initial_learning_rate = 0.001
lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
initial_learning_rate,
decay_steps=10,
decay_rate=0.92,
staircase=True
)
optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
model.compile(
optimizer=optimizer,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
这里有几个关键点:
我设置了两个重要的回调函数:
python复制checkpointer = ModelCheckpoint(
'best_model.weights.h5',
monitor='val_accuracy',
verbose=1,
save_best_only=True,
save_weights_only=True
)
earlystopper = EarlyStopping(
monitor='val_accuracy',
min_delta=0.001,
patience=20,
verbose=1
)
开始训练:
python复制epochs = 50
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[checkpointer, earlystopper]
)
训练过程中观察到:
避坑指南:如果验证准确率明显低于训练准确率,可能是过拟合了。可以尝试增加Dropout率、使用数据增强或减少模型复杂度。
绘制训练过程中的准确率和损失曲线:
python复制acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Accuracy')
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Loss')
plt.show()
从曲线可以看出:
加载最佳模型权重进行测试:
python复制model.load_weights('best_model.weights.h5')
img = Image.open("./Data/46-data/test/adidas/1.jpg")
image = tf.image.resize(img, [img_height, img_width])
img_array = tf.expand_dims(image, 0)
predictions = model.predict(img_array)
print("预测结果为:", class_names[np.argmax(predictions)])
测试结果正确识别出了Adidas的鞋子图片。
虽然模型已经能工作,但还有不少改进空间:
在实际项目中,我通常会先从一个简单模型开始,快速验证想法,然后再逐步优化。这种方法既能节省时间,又能避免一开始就陷入复杂的调参工作。