去年在超市自助结账区排队时,我注意到很多顾客都在为蔬菜识别错误而反复操作。这让我萌生了一个想法:能否用深度学习技术开发一个高精度的蔬菜识别系统?经过三个月的开发和优化,最终完成了这个基于ResNet50的蔬菜识别Web应用。系统不仅能准确识别8种常见蔬菜,还具备完整的用户管理功能,实测识别准确率达到96.7%。
这个系统特别适合以下几类人群:
系统采用经典的B/S架构,前端用HTML5+CSS3实现响应式布局,后端基于Python Flask框架开发RESTful API,核心算法使用TensorFlow实现的ResNet50模型。整套代码采用模块化设计,包含数据预处理、模型训练、Web接口和前端交互四个主要模块。
在模型选型阶段,我对比了VGG16、InceptionV3和ResNet50三个主流CNN模型。最终选择ResNet50主要基于三点考虑:
实测数据显示,在相同训练条件下:
原始ResNet50是为1000类ImageNet设计的,我们需要针对8种蔬菜进行微调(fine-tuning)。关键步骤包括:
python复制base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(224,224,3))
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(8, activation='softmax')(x) # 8类蔬菜
model = Model(inputs=base_model.input, outputs=predictions)
python复制train_datagen = ImageDataGenerator(
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
重要提示:微调时要冻结底层卷积层,只训练最后3层,避免小数据量下的过拟合问题。
系统采用前后端分离架构,分为四个核心层次:
code复制用户端 → Nginx → Flask API → TensorFlow模型 → MySQL
↑ ↑
Vue.js前端 Redis缓存
图像识别接口的核心代码逻辑:
python复制@app.route('/api/recognize', methods=['POST'])
@jwt_required()
def recognize():
if 'file' not in request.files:
return jsonify({"error": "No file uploaded"}), 400
file = request.files['file']
img = Image.open(file.stream)
img = img.resize((224,224))
# 预处理
img_array = np.array(img)/255.0
img_array = np.expand_dims(img_array, axis=0)
# 模型推理
predictions = model.predict(img_array)
class_idx = np.argmax(predictions[0])
# 记录到数据库
record = Recognition(
user_id=get_jwt_identity(),
filename=file.filename,
class_name=CLASS_NAMES[class_idx],
confidence=float(predictions[0][class_idx])
)
db.session.add(record)
db.session.commit()
return jsonify({
"class": CLASS_NAMES[class_idx],
"confidence": float(predictions[0][class_idx])
})
python复制# 启动时加载模型到内存
model = load_model('resnet50_vegetable.h5')
graph = tf.get_default_graph()
# 在请求处理中使用
global graph
with graph.as_default():
predictions = model.predict(img_array)
javascript复制// 使用canvas压缩图片
const compressImage = (file, quality) => {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onload = (event) => {
const img = new Image()
img.onload = () => {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
canvas.toBlob(resolve, 'image/jpeg', quality)
}
img.src = event.target.result
}
reader.readAsDataURL(file)
})
}
构建了包含8类蔬菜的数据集,每类约1200张图片:
code复制西红柿/
├── tomato_001.jpg # 完整果实
├── tomato_002.jpg # 切面
└── tomato_003.jpg # 带枝叶
通过组合增强提升模型鲁棒性:
python复制aug = iaa.Sequential([
iaa.Fliplr(0.5), # 50%水平翻转
iaa.Affine(
rotate=(-20, 20),
shear=(-15, 15)
),
iaa.AdditiveGaussianNoise(scale=0.05*255),
iaa.Crop(percent=(0, 0.1)) # 随机裁剪
])
实测发现:适度的高斯噪声和随机裁剪能显著提升模型对模糊图片的识别能力。
最佳参数组合:
python复制model.compile(
optimizer=Adam(lr=0.0001),
loss='categorical_crossentropy',
metrics=['accuracy']
)
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=val_generator,
validation_steps=50,
callbacks=[
EarlyStopping(patience=3),
ModelCheckpoint('best_model.h5', save_best_only=True)
]
)
关键发现:
推荐使用Docker compose编排服务:
yaml复制version: '3'
services:
web:
build: ./web
ports:
- "5000:5000"
depends_on:
- redis
- mysql
model:
image: tensorflow/serving
ports:
- "8501:8501"
volumes:
- ./models:/models
command: ["--model_name=veggie", "--model_base_path=/models"]
redis:
image: redis:alpine
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: example
使用Locust模拟100并发用户:
| 指标 | 数值 |
|---|---|
| 平均响应时间 | 218ms |
| 95%请求耗时 | 356ms |
| 最大QPS | 432次/秒 |
| 错误率 | 0.12% |
bash复制export TF_FORCE_GPU_ALLOW_GROWTH=true
python复制from flask_cors import CORS
CORS(app, resources={r"/api/*": {"origins": "*"}})
python复制import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
在实际使用中,我发现几个有价值的改进点:
kotlin复制// Android端模型量化
val converter = TensorFlowLiteConverter.fromSavedModel(modelPath)
converter.optimizations = [Optimize.DEFAULT]
val tfliteModel = converter.convert()
python复制# 在线学习新样本
model.fit(new_samples, epochs=1, batch_size=16)
这个项目最让我惊喜的是ResNet50对蔬菜局部特征的捕捉能力——即使只剩半截黄瓜,系统也能准确识别。建议初次尝试时先从3-5类蔬菜开始,逐步扩展类别。训练数据一定要包含各种光照条件和拍摄角度,这是提升模型泛化能力的关键。