基于Flask与大模型的智能健康顾问系统
目录
摘要
本文详细记录了基于Flask、PyTorch(ResNet50)和通义千问大模型构建皮肤病变智能健康顾问系统的全过程。系统支持图片上传与文字描述双模式输入,可对皮肤病变进行初步识别并生成个性化健康建议。文章不仅包含完整的代码实现,还深入分析了训练模型与大模型调用的本质区别,并分享了在受限网络环境下部署的踩坑与解决方案(WinError 1114、PyTorch版本兼容等)。适合AI初学者、Flask Web开发者及对医疗AI感兴趣的同学参考。
一、项目背景与核心痛点
1.1 为什么要做这个项目?
随着人们对健康管理的重视,能够快速获取初步健康建议的需求日益增长。然而,传统就医流程耗时长,而市面上已有的AI诊断工具往往存在以下问题:
-
输入模式单一:要么只能传图片,要么只能打字
-
建议生成生硬:缺乏人性化和个性化
-
部署门槛高:对硬件要求苛刻,环境配置复杂
1.2 本项目的核心目标
打造一款双模态输入 + 大模型赋能的轻量级健康顾问系统,让用户可以通过上传图片或描述症状,获得AI辅助诊断和个性化的健康建议。
1.3 技术选型与架构设计
|
模块 |
技术选型 |
选型理由 |
|---|---|---|
|
Web框架 |
Flask |
轻量灵活,适合小型应用快速开发 |
|
图像识别 |
ResNet50(微调) |
预训练模型迁移学习效果好,收敛快 |
|
大模型 |
通义千问(qwen-turbo) |
中文能力强,API兼容性好 |
|
图像处理 |
Pillow + Torchvision |
标准化预处理流程 |
|
部署方案 |
离线安装包 |
解决校园网环境下的依赖安装难题 |
二、系统架构与核心流程
2.1 整体架构图
纯文本
纯文本
用户输入(图片/文字)
↓
Flask路由分发
↓
┌─────────────────┐ ┌─────────────────┐
│ 图片模式 │ │ 文字模式 │
│ ResNet50推理 │ │ 通义千问API │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────────────────────────┐
│ 结果渲染与展示 │
│ 疾病名称 + 置信度 + 健康建议 │
│ + 免责声明 │
└─────────────────────────────────────┘
2.2 双模式输入设计思路
核心设计原则:两种模式互不干扰,用户根据实际情况自由选择。
-
图片模式:适用于已有皮肤病变照片的用户,利用微调模型进行快速分类
-
文字模式:适用于不方便拍照或症状描述更清晰的用户,调用大模型进行推理
三、核心代码实现与深度解析
3.1 训练模型代码(图像识别)
python
python
import torch
import torch.nn as nn
from torchvision import models, transforms
from PIL import Image
import os
# 设备检测:优先使用GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
def create_model(num_classes=7):
"""
创建ResNet50模型,替换全连接层
原理:冻结预训练权重,只训练新增的分类头
"""
model = models.resnet50(weights=None)
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Linear(num_features, 512),
nn.ReLU(),
nn.Dropout(0.3), # 防止过拟合
nn.Linear(512, num_classes)
)
return model
def load_model_with_fallback(model_path='best_skin_model.pth'):
"""
加载模型,带降级策略
解决:PyTorch 2.6+ 的 weights_only 安全机制
"""
if not os.path.exists(model_path):
raise FileNotFoundError(f"模型文件不存在: {model_path}")
try:
# 优先使用安全加载
checkpoint = torch.load(model_path, map_location=device, weights_only=True)
except:
# 降级:允许加载包含自定义类的模型
checkpoint = torch.load(model_path, map_location=device, weights_only=False)
model = create_model(checkpoint['num_classes'])
model.load_state_dict(checkpoint['model_state_dict'])
model.to(device)
model.eval()
return model, checkpoint['label_encoder']
代码解析:
-
create_model函数定义了模型结构,其中Dropout(0.3)表示在训练时随机丢弃30%的神经元,防止过拟合 -
load_model_with_fallback函数处理了PyTorch版本兼容问题:新版PyTorch默认启用weights_only=True安全机制,如果加载失败则降级为weights_only=False
3.2 图像预处理流水线
python
python
# 标准化预处理流水线
transform = transforms.Compose([
transforms.Resize((224, 224)), # ResNet50标准输入尺寸
transforms.ToTensor(), # 转换为Tensor并归一化到[0,1]
transforms.Normalize( # ImageNet标准化参数
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
def predict_image(model, image_path, label_encoder, disease_mapping):
"""
图片预测函数
返回:(疾病中文名, 置信度)
"""
try:
image = Image.open(image_path).convert('RGB')
input_tensor = transform(image).unsqueeze(0).to(device)
with torch.no_grad(): # 不计算梯度,节省内存
outputs = model(input_tensor)
probabilities = torch.nn.functional.softmax(outputs[0], dim=0)
predicted_idx = torch.argmax(probabilities).item()
confidence = probabilities[predicted_idx].item()
disease_code = label_encoder.inverse_transform([predicted_idx])[0]
disease_name = disease_mapping.get(disease_code, disease_code)
return disease_name, confidence
except Exception as e:
return f"预测失败: {str(e)}", 0.0
执行结果说明:
-
输入:一张皮肤病变图片
-
输出示例:
("黑素细胞痣", 0.9137) -
含义:模型认为该图片有91.37%的概率属于“黑素细胞痣”
3.3 大模型调用代码(文字建议)
python
python
from dashscope import Generation
def call_llm_for_diagnosis(symptom_text):
"""
调用通义千问大模型进行诊断
设计思路:结构化Prompt + 低温度参数保证稳定性
"""
system_prompt = "你是一名经验丰富的皮肤科医生,擅长根据症状描述进行初步诊断。"
user_prompt = f"""患者症状描述:{symptom_text}
请严格按照以下格式回答(每行一个要点):
【可能疾病】[疾病名称]
【置信度】[0-100之间的整数]
【健康建议】
- [建议1]
- [建议2]
- [建议3]
【注意事项】
- [注意1]
- [注意2]
【免责声明】⚠️ 以上建议仅供参考,须就医确认。"""
try:
response = Generation.call(
model="qwen-turbo",
prompt=f"{system_prompt}\n\n{user_prompt}",
max_tokens=800,
temperature=0.3, # 低温度:输出更稳定、更保守
)
result = response.output.text
# 正则提取关键信息
import re
disease = "未知"
confidence = 50
disease_match = re.search(r'【可能疾病】(.*?)(?:\n|$)', result)
if disease_match:
disease = disease_match.group(1).strip()
conf_match = re.search(r'【置信度】(\d+)', result)
if conf_match:
confidence = int(conf_match.group(1))
return disease, confidence / 100, result
except Exception as e:
print(f"大模型调用失败: {e}")
return fallback_keyword_match(symptom_text)
def fallback_keyword_match(text):
"""
关键词匹配降级方案
当大模型API不可用时,使用规则匹配保证系统可用
"""
text = text.lower()
patterns = {
'红斑|鳞屑|脱屑|瘙痒': ('银屑病', 0.65),
'水疱|疱疹|疼痛|灼热': ('带状疱疹', 0.68),
'痘痘|粉刺|脓疱|结节': ('痤疮', 0.72),
'湿疹|渗出|结痂|干燥': ('湿疹', 0.66),
}
best_match = "未知"
best_score = 0
for pattern, (disease, base_conf) in patterns.items():
keywords = pattern.split('|')
match_count = sum(1 for kw in keywords if kw in text)
if match_count > 0:
score = base_conf * (0.5 + 0.5 * match_count / len(keywords))
if score > best_score:
best_score = score
best_match = disease
return best_match, best_score, f"疑似:{best_match}(置信度:{best_score:.0%})\n建议前往医院就诊确诊。\n\n⚠️ 以上建议仅供参考,须就医确认。"
代码解析:
-
temperature=0.3:低温度参数使模型输出更稳定、更保守,避免胡言乱语 -
fallback_keyword_match:当大模型API不可用时,使用关键词匹配作为降级方案,保证系统在任何情况下都能返回结果
四、训练模型 vs 大模型:本质区别详解
很多初学者容易混淆这两个概念,这里我用最直观的方式解释:
|
对比项 |
训练模型(ResNet50) |
大模型(通义千问) |
|---|---|---|
|
本质 |
自己训练的小模型 |
调用别人训练好的大模型API |
|
数据 |
需要几千张图片训练 |
不需要训练数据 |
|
硬件 |
需要GPU(显卡) |
不需要GPU,联网就行 |
|
成本 |
训练耗时几小时 |
每次调用几分钱 |
|
输出 |
固定的7类疾病 |
任意文本,灵活回答 |
|
速度 |
毫秒级 |
秒级(需网络传输) |
打个比方:
-
训练模型:就像你自己教一个学生认识皮肤病,给他看1000张图片,反复考试直到他学会
-
大模型:就像你直接打电话问一位专家医生,描述症状,他凭经验直接告诉你答案
在项目中的分工:
-
用户上传图片 → 训练模型负责识别 → 输出疾病名称
-
用户输入文字 → 大模型负责推理 → 输出健康建议
五、环境配置与踩坑记录
5.1 依赖安装优化
问题:教室网络慢,pip install经常超时中断。
解决方案:使用清华源 + 指定CPU版本PyTorch。
bash
bash
# 强制安装CPU版PyTorch(体积小一半)
pip install torch torchvision torchaudio \
--index-url https://download.pytorch.org/whl/cpu \
-i https://pypi.tuna.tsinghua.edu.cn/simple
# 其他依赖
pip install flask pillow numpy requests dashscope \
-i https://pypi.tuna.tsinghua.edu.cn/simple
5.2 WinError 1114 DLL报错(经典坑)
问题现象:
纯文本
纯文本
OSError: [WinError 1114] 动态链接库(DLL)初始化例程失败
Error loading "c10.dll" or one of its dependencies
根因分析:Windows系统缺少Visual C++ Redistributable运行库,导致PyTorch的底层C++代码无法加载。
解决方案:
-
下载VC++运行库:https://aka.ms/vs/17/release/vc_redist.x64.exe
-
安装后必须重启电脑
-
如果仍报错,强制重装PyTorch:
bash
bash
pip uninstall torch torchvision torchaudio -y
pip install torch torchvision torchaudio \
--index-url https://download.pytorch.org/whl/cpu \
--force-reinstall \
-i https://pypi.tuna.tsinghua.edu.cn/simple
5.3 模型加载版本兼容
问题:PyTorch 2.6+ 默认启用 weights_only=True,导致加载包含自定义类的模型时失败。
解决方案:添加降级逻辑(已在代码中实现)。
六、运行效果与测试
6.1 启动日志
纯文本
纯文本
✅ 使用设备: cpu
🔄 正在加载模型...
✅ 模型加载成功!
类别数: 7
验证准确率: 85.23%
==================================================
🚀 启动Web服务...
🌐 请在浏览器中访问: http://127.0.0.1:5000
==================================================
6.2 功能测试
|
测试项 |
输入 |
预期输出 |
实际结果 |
|---|---|---|---|
|
图片上传 |
黑素细胞痣图片 |
黑素细胞痣,置信度>80% |
✅ 通过 |
|
文字描述 |
"手臂出现红斑,很痒" |
可能湿疹,置信度>70% |
✅ 通过 |
|
非法输入 |
空白图片 |
提示"图片不存在" |
✅ 通过 |
|
大模型超时 |
超长文本 |
降级到关键词匹配 |
✅ 通过 |
6.3 界面截图指引
📸 建议截图1:浏览器打开
http://127.0.0.1:5000后的首页界面📸 建议截图2:上传图片后显示诊断结果的页面
📸 建议截图3:输入文字描述后大模型返回建议的页面
📸 建议截图4:遇到WinError 1114报错时的黑窗口截图
七、总结与展望
7.1 项目亮点
-
双模态输入:支持图片和文字两种咨询方式,覆盖更多使用场景
-
大模型赋能:利用LLM生成结构化、个性化的健康建议
-
完善的降级策略:大模型失败时自动切换关键词匹配,保证系统鲁棒性
-
离线部署方案:解决校园网环境下的依赖安装难题
7.2 技术收获
-
掌握了PyTorch模型加载与推理的全流程
-
学会了Flask Web开发与前后端交互
-
积累了Prompt工程与大模型调用的实战经验
-
锻炼了环境配置与问题排查的能力
7.3 未来改进方向
-
引入更多专科模型(皮肤、眼科、口腔等)
-
集成语音输入功能
-
建立用户反馈机制,持续优化诊断准确率
更多推荐




所有评论(0)