先把手头的咖啡杯放下,想象一下:你拍一张照片发给电脑,它三秒后告诉你“这是可回收物”。听起来像魔法,但背后其实就是你正在学的东西——深度学习。它不是玄学,也不是只有计算机博士才能碰的玩意儿。今天我们就从零开始,把这套逻辑拆成能跑、能改、能继续往上搭的实战代码。
先别怕数学,AI其实是在“看图画猜答案”
教小朋友认数字,你会怎么做?拿出一张卡片写个“3”,说:“这是3。”再拿一张写“3”但笔画歪一点的,再说一遍。看多了,他自己就总结出规律:三条弯线、上面开口、下面封口……这就是深度学习在干的事。
神经网络里有一堆“旋钮”(专业名叫权重)。刚开始旋钮乱拧,模型猜啥都错。每猜错一次,系统就悄悄把旋钮往“下次别错”的方向拧一点。这个“拧”的过程叫训练,拧的幅度由损失函数和学习率决定。你不需要手算梯度,TensorFlow和Keras已经帮你把最苦的活干了,你只需要把数据喂进去、把结构搭好、把训练跑起来。
你的第一行代码:让电脑学会认数字
如果你还没装过Python,建议直接用VS Code或者Jupyter Lab。电脑配置一般也没关系,MNIST数据集很小,CPU就能跑得飞快;等后面做垃圾分类时,再开Google Colab白嫖GPU,或者用本地带N卡的环境。
先把依赖装上:
pip install tensorflow pandas matplotlib pillow ipykernel
下面这段代码可以直接在Jupyter Notebook里跑。它的任务是加载MNIST手写数字集,训练一个微型卷积神经网络,然后让你亲手试几张预测结果。
import tensorflow as tf
import matplotlib.pyplot as plt
# 1. 加载数据:MNIST是28x28像素的手写数字,共6万张训练图+1万张测试图
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 2. 预处理:像素原本是0~255的整数,神经网络更喜欢0~1之间的小数
x_train, x_test = x_train / 255.0, x_test / 255.0
# 卷积层需要“通道数”维度,黑白图片补上1
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]
# 3. 搭模型:像流水线一样,一层一层处理图像
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.3), # 防止死记硬背,随机“屏蔽”一部分神经元
tf.keras.layers.Dense(10, activation='softmax') # 输出0~9每个数字的概率
])
model.summary()
# 4. 编译:告诉模型怎么评估自己、怎么更新旋钮
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 5. 训练:epochs=5对MNIST来说已经足够漂亮,不用贪多
history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))
# 6. 验证集表现
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"测试集准确率:{test_acc:.4f}")
# 7. 挑几张真实图片看看预测效果
plt.figure(figsize=(8,3))
for i in range(6):
pred = model.predict(x_test[i:i+1])[0]
guess = pred.argmax()
confidence = pred.max()
plt.subplot(2,3,i+1)
plt.imshow(x_test[i].squeeze(), cmap='gray')
plt.title(f"真实:{y_test[i]} | 预测:{guess} ({confidence:.1%})")
plt.axis('off')
plt.tight_layout()
plt.show()
跑完你会发现,准确率轻松破98%。这时候你其实已经摸到了深度学习的门槛:数据进来 → 特征被一层层提取 → 最后输出概率分布。后面垃圾分类,只是把“数字”换成“物体”,把“黑白小图”换成“彩色照片”。
从认数字到认垃圾,模型该怎么“长大”?
垃圾分类的数据集结构通常长这样,你只需要在电脑上建好文件夹:
garbage_data/
├── recyclable/ # 可回收物
│ ├── bottle1.jpg
│ └── paper2.jpg
├── kitchen/ # 厨余垃圾
├── hazardous/ # 有害垃圾
└── other/ # 其他垃圾
每个文件夹里的图片数量尽量均衡。如果某类只有几十张,模型会偏向它,后面预测就不公平。
下面这段代码会把图片自动按文件夹分类加载,做数据增强,训练一个能直接用的分类器,并保存成.keras文件。
import tensorflow as tf
import os
# 替换成你电脑上的实际路径
DATA_DIR = "./garbage_data"
IMG_SIZE = (224, 244) # 统一尺寸,ResNet/MobileNet这类常用224
BATCH_SIZE = 32
EPOCHS = 10
# 1. 自动按目录加载数据,并划分训练/验证集(80%/20%)
train_ds = tf.keras.utils.image_dataset_from_directory(
DATA_DIR, validation_split=0.2, subset="training",
seed=42, image_size=IMG_SIZE, batch_size=BATCH_SIZE
)
val_ds = tf.keras.utils.image_dataset_from_directory(
DATA_DIR, validation_split=0.2, subset="validation",
seed=42, image_size=IMG_SIZE, batch_size=BATCH_SIZE
)
# 2. 数据增强:旋转、翻转、亮度微调,相当于给模型戴不同眼镜看同一件物品
data_augmentation = tf.keras.Sequential([
tf.keras.layers.RandomFlip("horizontal"),
tf.keras.layers.RandomRotation(0.15),
tf.keras.layers.RandomContrast(0.2),
])
# 3. 把类别名读出来,后面预测结果要显示中文标签
class_names = train_ds.class_names
print("分类标签:", class_names)
# 4. 构建模型:前面是特征提取器,后面是分类头
inputs = tf.keras.Input(shape=(*IMG_SIZE, 3))
x = data_augmentation(inputs)
x = tf.keras.layers.Conv2D(32, 3, activation='relu')(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Conv2D(64, 3, activation='relu')(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Conv2D(128, 3, activation='relu')(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Dropout(0.4)(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(256, activation='relu')(x)
outputs = tf.keras.layers.Dense(len(class_names), activation='softmax')(x)
garbage_model = tf.keras.Model(inputs, outputs)
garbage_model.summary()
# 5. 编译与训练
garbage_model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# 6. 加一个早停机制:验证集不再进步就自动停,省时间防过拟合
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)
history = garbage_model.fit(
train_ds, validation_data=val_ds,
epochs=EPOCHS, callbacks=[early_stop]
)
# 7. 保存模型,后面推理直接加载
model_path = "./garbage_classifier.keras"
garbage_model.save(model_path)
print(f"模型已保存到 {model_path}")
训练完之后,写一个小脚本就能拍照识别。注意这里用的是tf.data管道,比传统load_img快得多,也更适合后续接摄像头或手机App。
import tensorflow as tf
import numpy as np
model = tf.keras.models.load_model("./garbage_classifier.keras")
class_names = ["recyclable", "kitchen", "hazardous", "other"] # 按你文件夹顺序
def predict_garbage(image_path):
img = tf.keras.utils.load_img(image_path, target_size=(224, 224))
arr = tf.keras.utils.img_to_array(img)
arr = np.expand_dims(arr, axis=0) / 255.0 # 归一化
probs = model.predict(arr)[0]
idx = np.argmax(probs)
return class_names[idx], float(probs[idx])
label, conf = predict_garbage("./test_bottle.jpg")
print(f"识别结果:{label}(置信度 {conf:.2%})")
如果你发现准确率卡在70%左右上不去,别急着换模型。先检查三件事:图片有没有混入错误标签、训练集和验证集是否真的随机打乱、数据增强是不是太猛把物体特征扭曲了。深度学习调试,80%的时间花在数据和日志上,20%才花在改网络结构上。
真正跑起来时,这几个坑我帮你提前填了
新手最容易栽的地方,往往不是代码写不出来,而是细节没对齐。
像素范围没归一化。 图片默认是0到255,直接喂进网络会让梯度爆炸,损失曲线像过山车。除以255.0是行业惯例,别嫌麻烦。
类别顺序和文件夹名对不上。 image_dataset_from_directory是按字母排序分配索引的。如果你的文件夹叫有害垃圾和hazardous混着放,打印出来的class_names可能和你以为的不一样。训练前务必print()确认。
过拟合:训练集99%,验证集60%。 这说明模型在“背答案”。解决办法:加Dropout、加数据增强、减少模型层数、提前停止训练。记住,验证集 loss 开始反弹的那一刻,就该停了。
显存不够。 如果本地GPU报OOM,把BATCH_SIZE从32降到16或8,或者把IMG_SIZE从224降到160。精度会略降,但能跑起来永远比报错强。
想上速度,可以换迁移学习。 当你有几千张垃圾分类图片时,直接训CNN不如加载一个预训练好的MobileNetV2或EfficientNet,只训练最后一层。代码改动很小:
base = tf.keras.applications.MobileNetV2(input_shape=(*IMG_SIZE,3), include_top=False, weights='imagenet')
base.trainable = False # 冻结 pretrained 部分
x = base(inputs, training=False)
x = tf.keras.layers.GlobalAveragePooling2D()(x)
outputs = tf.keras.layers.Dense(len(class_names), activation='softmax')(x)
transfer_model = tf.keras.Model(inputs, outputs)
预训练模型已经在几千万张图上见过各种形状、纹理、颜色,你只需要教它“这些特征对应哪类垃圾”。
接下来你可以怎么折腾
代码跑通只是起点。真正让项目有“产品感”,你可以接着做这几件事:
- 用
TensorBoard看训练曲线。命令行跑一句tensorboard --logdir ./logs,浏览器里打开就能看到准确率、损失、学习率变化,比干瞪眼猜原因靠谱得多。 - 把模型导出成TFLite或ONNX,接树莓派、Android或微信小程序。垃圾分类助手如果是给小区用的,手机端推理延迟最好控制在200毫秒以内。
- 给模型加个“不确定就让人工复核”的阈值。比如最高概率低于0.75,就返回“无法判断,请人工分拣”,这比硬塞一个答案更负责任。
- 收集真实环境照片做持续迭代。实验室数据和户外光线、反光、遮挡完全不一样,定期用新数据微调,模型才会越用越准。
深度学习不是黑箱,它更像一块需要你不断调教的画布。你现在写的每一行model.fit(),都是在教机器从混乱的像素里找出秩序。先把MNIST跑通,再把垃圾图片喂进去,你会明显感觉到那种“看着它学会”的成就感。遇到报错别慌,把错误信息前半段复制出来搜,90%的问题前人已经踩过。代码在这里,环境配好,点运行,剩下的交给时间。
