news 2026/6/8 16:29:14

皮肤病AI诊断系统:Vue前端+Flask推理+SpringBoot业务管理,含ISIC2019模型、Docker一键部署与完整开发资料

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
皮肤病AI诊断系统:Vue前端+Flask推理+SpringBoot业务管理,含ISIC2019模型、Docker一键部署与完整开发资料

本文还有配套的精品资源,点击获取

简介:直接可运行的皮肤病图像识别系统,前端用Vue.js搭建交互界面,支持图片上传、实时结果显示和诊断报告查看;后端分两层:Flask服务加载ONNX格式深度学习模型(基于ISIC2019数据集训练),专注图像预处理与皮肤病变分类(如黑色素痣、基底细胞癌、血管病变、良性角化病等);SpringBoot服务负责用户登录、病例存档、报告生成及MySQL数据管理。所有模块均提供标准化Dockerfile和docker-compose.yml,一条命令启动全部服务(Vue+Nginx、Flask、SpringBoot、MySQL)。资源包内置真实测试图例、训练好的ONNX模型文件、前后端完整源码、数据库初始化SQL脚本、API接口文档、Nginx配置示例、Docker部署说明和详细开发指南,适配高校课程设计、毕设开发或医疗AI原型快速验证。本地已实测通过多轮功能验证,无需额外配置即可启动使用,后续可灵活替换模型或接入新数据源。

1. 这不是又一个“AI看病”的Demo,而是一套能真正跑起来的医疗影像识别工作流

我带过三届本科生毕设,也帮本地两家社区医院做过皮肤科辅助工具原型。见过太多所谓“皮肤病AI系统”:前端页面做得像医疗App,后端却只用Jupyter Notebook跑个predict(),模型权重文件藏在GitHub私有仓库里,部署文档写着“请自行配置CUDA环境”,数据库连建表语句都不给——这种项目,答辩现场一演示就崩,学生自己都讲不清batch_size为什么设成8,更别说让医生点开网页上传一张手机拍的痣图、立刻看到分类概率和参考建议。

这套系统不一样。它从第一天设计起,就锚定一个目标:让一个没碰过Docker的学生,在Windows笔记本上插着网线,30分钟内把整套服务跑起来,上传一张“黑色素痣.png”,看到“MEL: 0.92, BCC: 0.03, VASC: 0.01”的结果,并在SpringBoot后台里查到这条病例记录、生成PDF报告、导出为Excel。

关键词里的“皮肤病识别”不是泛泛而谈——它对应ISIC2019数据集里7大类真实临床标注:黑色素瘤(MEL)、基底细胞癌(BCC)、良性角化病(BKL)、血管病变(VASC)、日光性角化病(AKIEC)、鳞状细胞癌(SCC)、黑色素痣(NV)。模型不是网上随便下载的ResNet50预训练权重,而是基于ISIC2019完整训练集微调后导出的ONNX格式,输入尺寸固定为224×224,输出是7维logits向量,经Softmax后给出每类概率。Flask不干别的,就做三件事:接收multipart/form-data图片、用OpenCV+Torchvision做标准化预处理(归一化、中心裁剪、转Tensor)、喂给ONNX Runtime推理并返回JSON结果。SpringBoot也不碰图像,只管人、图、报告三者关系:用户注册登录、上传记录绑定、诊断结果存档、PDF模板渲染、MySQL事务控制。Vue前端所有API调用都封装在services目录下,错误统一拦截,加载状态精确到按钮级,连“正在上传…”和“模型推理中…”的提示文案都按临床场景写了两版(简洁版给医生快速看,详细版给学生调试用)。

它适合谁?如果你是计算机专业学生,正为毕设发愁,这套代码就是你的“安全垫”——前后端分离清晰,Dockerfile逐行注释,数据库脚本带中文字段注释,连Nginx.conf里location /api/ 和 /backend/ 的反向代理路径都标了为什么这么写;如果你是医学信息工程方向的老师,想带学生做AI辅助诊断实践,它提供了可验证的临床数据闭环:真实图片→标准预处理→模型推理→结构化存档→报告输出;如果你是基层医院信息科人员,想评估AI工具落地成本,你会发现整套服务内存占用不到1.2GB,CPU峰值仅2核,MySQL单表设计合理,连备份脚本都放在init/目录下。它不承诺替代医生,但能让你第一次亲手把“皮肤病识别”从PPT变成浏览器里可点击、可验证、可审计的真实系统。

2. 系统架构设计与模块职责拆解:为什么必须是“双后端”,而不是一个Flask全包?

2.1 双服务架构的底层逻辑:隔离关注点,守住医疗系统的可靠性底线

很多人第一反应是:“干嘛搞两个后端?Flask加个SQLAlchemy不就全搞定了?”——这恰恰是医疗AI项目最容易踩的坑。我去年帮某三甲医院信息科评审一个类似项目,他们用单Flask服务既做图像推理又管用户权限,结果上线第三天,一位皮肤科主任上传了20张高清图批量分析,Flask进程因GPU显存不足OOM崩溃,整个用户登录接口跟着挂了37分钟。事后复盘发现:图像推理是计算密集型任务,耗时波动大(单图200ms~2s),且依赖GPU或ONNX Runtime优化;而用户管理、报告生成是IO密集型任务,要求响应稳定(<300ms)、事务强一致、审计日志完备。把两者塞进同一个进程,等于让急诊室和药房共用一台电脑——挂号排队时,药剂师没法配药;配药卡顿了,挂号窗口直接黑屏。

所以这套系统强制拆成双服务:
-Flask服务(onnx_app/):纯推理管道。它不连数据库,不处理用户会话,不生成PDF,只做一件事——把图片变成概率。启动时加载ONNX模型到内存(ONNX Runtime默认使用CPU,如需GPU加速只需改一行provider参数),所有HTTP接口无状态,支持横向扩展(docker-compose.yml里可直接scale flask=3)。它的Dockerfile甚至删掉了pip install flask-sqlalchemy,因为根本用不上。
-SpringBoot服务(springboot_backend/):业务中枢。它通过RestTemplate调用Flask的/api/v1/predict接口获取诊断结果,再将原始图片、用户ID、诊断JSON、时间戳等写入MySQL四张核心表(user、case_record、diagnosis_result、report_pdf)。所有数据库操作包裹在@Transactional中,PDF生成用Thymeleaf+IText7,确保“上传→诊断→存档→出报告”是原子操作。它的健康检查端点/actuator/health会同时探测MySQL连接和Flask服务可达性,任一失败即标记DOWN。

提示:双服务通信不是靠硬编码URL。SpringBoot的application.yml里配置了flask.service.url=http://flask:5000,docker-compose.yml中service名flask自动成为容器内DNS名称。这样即使你把Flask换成FastAPI,只要保持相同API契约,SpringBoot代码一行不用改。

2.2 模型选型与ISIC2019适配:为什么用ONNX而非PyTorch原生模型?

ISIC2019数据集包含25,331张皮肤镜图像,涵盖7类病变,每张图都有专家标注。原始论文多用ResNet50或EfficientNet-B3,但直接部署PyTorch模型有三大硬伤:
1.依赖地狱:PyTorch 1.13+需匹配特定CUDA版本,而Docker基础镜像ubuntu:22.04默认CUDA驱动可能不兼容;
2.冷启动慢:PyTorch JIT模型首次推理需编译,延迟高达3秒以上,影响用户体验;
3.跨平台弱:同一.pth文件在Windows/Mac/Linux上可能因算子实现差异导致精度漂移。

ONNX完美规避这些问题。我们采用的模型是:在PyTorch中用ISIC2019训练好的EfficientNet-B3,经torch.onnx.export()导出,关键参数设置为:

torch.onnx.export( model, dummy_input, # torch.randn(1, 3, 224, 224) "model/model.onnx", input_names=["input"], output_names=["output"], dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, opset_version=12 # 兼容ONNX Runtime 1.15+ )

导出后用onnx.checker.check_model()验证结构,再用onnxruntime.InferenceSession()加载。实测对比:PyTorch原生模型首帧推理3.2s,ONNX Runtime仅0.41s,且后续帧稳定在0.18±0.02s。更重要的是,ONNX Runtime提供CPU/GPU/ARM多后端支持,docker-compose.yml中只需修改flask服务的environment:

environment: - ONNX_PROVIDER=cuda # 启用GPU(需nvidia-docker) # - ONNX_PROVIDER=cpu # 默认CPU模式,笔记本友好

注意:资源包中的model/目录下不仅有.onnx文件,还有preprocess.py——它定义了与训练时完全一致的预处理流程:BGR→RGB转换(OpenCV读图默认BGR)、缩放至256×256、中心裁剪224×224、归一化(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])。这个文件被Flask的app.py直接import,确保线上推理与训练数据分布零偏差。

2.3 Docker化设计哲学:每个容器只做一件事,且这件事必须可验证

整套系统共4个容器,严格遵循Unix哲学“do one thing and do it well”:
-nginx:静态资源服务器(Vue打包后的dist/)+ 反向代理(/ → Vue, /api/ → Flask, /backend/ → SpringBoot)
-flask:ONNX推理服务(暴露5000端口)
-springboot:业务服务(暴露8080端口)
-mysql:数据库(暴露3306端口)

docker-compose.yml不是简单罗列services,而是构建了生产级依赖链:

services: nginx: depends_on: - flask - springboot # 启动前等待flask和springboot健康检查通过 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 5

MySQL初始化不是靠command: mysqld --init-file这种不可靠方式,而是用独立的init-mysql服务:

init-mysql: image: mysql:8.0 volumes: - ./init/init.sql:/docker-entrypoint-initdb.d/init.sql command: '--default-authentication-plugin=mysql_native_password' depends_on: [mysql]

init.sql包含创建database、user、grant权限、建表(含中文注释)、插入初始管理员账号(admin/admin123)四步,执行完自动退出。这样即使MySQL容器重启,数据不会丢失,初始化只运行一次。

实操心得:我在测试时发现,若直接在mysql容器中挂载SQL脚本,Docker会因权限问题报错。正确做法是用独立init容器,通过network共享mysql网络,用root用户执行初始化——这是Docker官方推荐的初始化模式,比任何“chmod 777”都可靠。

3. 核心模块实现详解:从图片上传到PDF报告的完整链路

3.1 Vue前端:不只是界面,更是临床工作流的数字化映射

Vue项目(vue_app/)采用Vite构建,结构清晰:

src/ ├── api/ # 所有API请求封装 │ ├── flask.js # 调用/flask/predict │ └── backend.js # 调用/springboot/xxx ├── components/ # 可复用UI组件 │ ├── UploadArea.vue # 拖拽上传区(支持单图/多图) │ ├── ResultCard.vue # 诊断结果卡片(带概率条、置信度颜色编码) │ └── ReportPreview.vue # PDF报告预览(嵌入iframe) ├── views/ # 页面路由 │ ├── HomeView.vue # 首页:上传入口+最近3条记录 │ ├── HistoryView.vue # 历史记录页(分页+搜索) │ └── ReportView.vue # 报告详情页(含打印按钮) └── stores/ # Pinia状态管理 └── caseStore.js # 管理当前病例ID、诊断结果、PDF URL

关键细节在于临床语义的精准表达
- 上传区域明确提示:“请上传皮肤镜图像(非手机直拍),分辨率建议≥1024×768,格式为JPG/PNG”。这是根据ISIC2019数据集特性写的——该数据集所有图像均为专业皮肤镜采集,直拍图因光照、角度差异会导致模型性能断崖式下跌。我们在UploadArea.vue中嵌入了客户端校验:

const validateImage = (file) => { if (!['image/jpeg', 'image/png'].includes(file.type)) { ElMessage.error('仅支持JPG/PNG格式'); return false; } const img = new Image(); img.src = URL.createObjectURL(file); img.onload = () => { if (img.width < 1024 || img.height < 768) { ElMessage.warning('图像分辨率低于推荐值,可能影响诊断准确性'); } }; return true; };
  • 结果卡片采用临床分级色标:MEL(黑色素瘤)概率>0.7显示红色警示框,0.5~0.7为橙色预警,<0.5为绿色正常提示;BCC(基底细胞癌)同理。这不是随意配色,而是参照《皮肤肿瘤诊疗指南》中风险分层标准设定的视觉编码。
  • PDF报告预览页集成jsPDFhtml2canvas,点击“生成报告”按钮后,前端将ResultCard内容渲染为Canvas,再转为PDF二进制流,最后调用SpringBoot的/api/v1/report/generate接口提交——注意,这里不是把图片传给后端,而是传Canvas生成的base64字符串,后端仅负责拼接模板、存库、返回PDF URL。这样设计既减轻后端带宽压力,又保证前端可实时预览。

注意:gemini_api-key.js文件是预留的扩展接口,当前未启用。它用于未来接入多模态模型(如Gemini Vision)分析图文报告,但当前版本所有诊断均基于纯图像模型,避免引入不可控变量。

3.2 Flask推理服务:轻量、确定、可审计的AI管道

onnx_app/app.py只有187行,却覆盖了工业级推理服务的所有要素:

1. 模型加载与缓存

# 全局变量,应用启动时加载一次 session = None input_name = None output_name = None @app.before_first_request def load_model(): global session, input_name, output_name model_path = os.path.join(os.path.dirname(__file__), "model", "efficientnet_b3_isic2019.onnx") session = ort.InferenceSession(model_path, providers=['CPUExecutionProvider']) input_name = session.get_inputs()[0].name output_name = session.get_outputs()[0].name

before_first_request确保模型只加载一次,避免每次请求都重复IO。providers参数明确指定CPU执行,杜绝GPU驱动冲突。

2. 图像预处理流水线
util/preprocess.py定义了与训练完全一致的流程:

def preprocess_image(image_bytes): nparr = np.frombuffer(image_bytes, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # BGR img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR→RGB img = cv2.resize(img, (256, 256)) img = img[16:240, 16:240] # 中心裁剪224×224 img = img.astype(np.float32) / 255.0 img = (img - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] img = np.transpose(img, (2, 0, 1)) # HWC→CHW img = np.expand_dims(img, axis=0) # 添加batch维度 return img

关键点:所有数值运算用np.float32,避免float64导致ONNX Runtime报错;裁剪坐标[16:240, 16:240]精确对应256→224的中心裁剪((256-224)/2=16),不是近似值。

3. 推理接口与错误防御

@app.route('/api/v1/predict', methods=['POST']) def predict(): try: if 'file' not in request.files: return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No selected file'}), 400 image_bytes = file.read() if len(image_bytes) == 0: return jsonify({'error': 'Empty file'}), 400 # 预处理 input_tensor = preprocess_image(image_bytes) # ONNX推理 start_time = time.time() outputs = session.run([output_name], {input_name: input_tensor}) inference_time = time.time() - start_time # Softmax并取top3 probs = softmax(outputs[0][0]) class_names = ['MEL', 'NV', 'BCC', 'AKIEC', 'BKL', 'DF', 'VASC'] # ISIC2019顺序 top3_idx = np.argsort(probs)[-3:][::-1] result = { 'inference_time_ms': round(inference_time * 1000, 2), 'predictions': [ {'class': class_names[i], 'probability': float(probs[i])} for i in top3_idx ] } return jsonify(result) except Exception as e: app.logger.error(f"Prediction error: {str(e)}") return jsonify({'error': 'Internal server error'}), 500
  • 返回inference_time_ms供前端监控性能;
  • softmax用NumPy手动实现(np.exp(x) / np.sum(np.exp(x))),避免依赖PyTorch;
  • 错误日志记录到app.logger,Docker日志可直接捕获。

3.3 SpringBoot业务服务:医疗数据的强一致性守护者

springboot_backend/src/main/java/com/dermatology/下核心结构:

controller/ ├── UserController.java # /api/v1/user/ 登录注册 ├── CaseController.java # /api/v1/case/ 病例增删改查 └── ReportController.java # /api/v1/report/ PDF生成与下载 service/ ├── UserService.java # 用户密码BCrypt加密 ├── CaseService.java # 病例业务逻辑(含事务) └── ReportService.java # PDF生成(Thymeleaf模板+IText7) repository/ └── CaseRepository.java # JPA接口,对应case_record表 entity/ └── CaseRecord.java # @Entity映射,含@CreatedDate等审计字段

关键实现:
-病例创建事务(CaseService.java):

@Transactional public CaseRecord createCase(Long userId, MultipartFile imageFile, String diagnosisJson) { // 1. 保存原始图片到static/upload/(相对路径,Nginx可直接访问) String fileName = UUID.randomUUID().toString() + "_" + imageFile.getOriginalFilename(); Path uploadPath = Paths.get("static/upload/", fileName); Files.createDirectories(uploadPath.getParent()); Files.write(uploadPath, imageFile.getBytes()); // 2. 创建病例实体 CaseRecord caseRecord = new CaseRecord(); caseRecord.setUserId(userId); caseRecord.setImagePath("/upload/" + fileName); // Nginx静态路径 caseRecord.setDiagnosisResult(diagnosisJson); caseRecord.setCreatedAt(LocalDateTime.now()); // 3. 保存到数据库 return caseRepository.save(caseRecord); }
  • PDF报告生成(ReportService.java):
public byte[] generateReport(Long caseId) throws Exception { CaseRecord caseRecord = caseRepository.findById(caseId) .orElseThrow(() -> new RuntimeException("Case not found")); // 渲染Thymeleaf模板 Context context = new Context(); context.setVariable("case", caseRecord); context.setVariable("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); String html = templateEngine.process("report-template", context); // HTML转PDF PdfWriter writer = new PdfWriter(new ByteArrayOutputStream()); PdfDocument pdfDoc = new PdfDocument(writer); HtmlConverter.convertToPdf(html, pdfDoc); return pdfDoc.getWriter().getOutputStream().toByteArray(); }

模板report-template.html包含医院Logo占位符、患者基本信息区、诊断结果表格(按概率降序)、临床建议段落(根据主诊断类别动态填充,如MEL显示“建议皮肤镜随访或活检”,BCC显示“建议手术切除”)。

实操心得:MySQL字符集必须设为utf8mb4,否则中文PDF模板中的“建议”二字会乱码。我们在init.sql中明确写了:
sql CREATE DATABASE IF NOT EXISTS dermatology DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

4. Docker一键部署全流程与避坑指南

4.1 本地部署四步法:从零到全服务运行

前提条件:已安装Docker Desktop(Windows/Mac)或Docker Engine(Linux),且Docker服务正在运行。

步骤1:克隆仓库并进入目录

git clone https://github.com/xxx/4HYmjVkxUthJZdR6fGv8-master-a16ed0447a157769e53e26a24c02154efbba2ad8.git cd 4HYmjVkxUthJZdR6fGv8-master-a16ed0447a157769e53e26a24c02154efbba2ad8

步骤2:构建前端镜像(Vue+Nginx)

cd vue_app npm install npm run build # 生成dist/目录 cd .. # 构建nginx镜像(Dockerfile在vue_app/目录下) docker build -t dermatology-nginx -f vue_app/Dockerfile .

注意:vue_app/Dockerfile基于nginx:alpine,将dist/拷贝到/usr/share/nginx/html,并覆盖默认nginx.conf。其中关键配置:
nginx location /api/ { proxy_pass http://flask:5000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /backend/ { proxy_pass http://springboot:8080/; proxy_set_header Host $host; }
这样前端访问/api/v1/predict实际转发到http://flask:5000/v1/predict,无需跨域。

步骤3:构建Flask镜像

cd onnx_app docker build -t dermatology-flask . cd ..

onnx_app/Dockerfile关键点:

FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 模型文件体积大,放在最后减少镜像层缓存失效 COPY model/ ./model/ EXPOSE 5000 CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]

使用gunicorn替代flask run,支持多worker并发,--workers 2适配笔记本双核。

步骤4:启动整套服务

# 确保在项目根目录(docker-compose.yml所在位置) docker-compose up -d # 查看服务状态 docker-compose ps # 查看日志(重点关注init-mysql和springboot) docker-compose logs -f init-mysql springboot

等待约90秒,当docker-compose ps显示所有服务状态为Up (healthy),即可访问:
- 前端:http://localhost (Vue首页)
- Flask健康检查:http://localhost:5000/health (返回{“status”:”ok”})
- SpringBoot健康检查:http://localhost:8080/actuator/health (返回UP状态)

提示:首次启动时,init-mysql容器会执行SQL初始化,此时springboot可能因数据库未就绪而重试连接。docker-compose.yml中已配置restart: on-failure:5,无需人工干预。

4.2 常见问题排查速查表

问题现象可能原因排查命令解决方案
访问http://localhost空白页,控制台报404Nginx未正确挂载dist/或配置错误docker exec -it dermatology-nginx ls /usr/share/nginx/html检查vue_app/dist/是否存在,确认Dockerfile中COPY路径正确
上传图片后返回500,Flask日志显示”ModuleNotFoundError: No module named ‘onnxruntime’“Flask镜像未安装ONNX Runtimedocker exec -it dermatology-flask pip list \| grep onnx修改onnx_app/requirements.txt,添加onnxruntime==1.15.1,重新build
SpringBoot启动失败,日志报”Access denied for user ‘root’@’springboot’“MySQL初始化未完成或密码错误docker exec -it dermatology-mysql mysql -uroot -proot123 -e "show databases;"检查init.sql中CREATE USER语句,确保用户名密码与springboot/application.yml中spring.datasource.password一致
诊断结果概率全为0.0,或top3类别明显错误图像预处理与训练不一致在Flask容器内运行python util/test_preprocess.py(资源包提供)对比test_preprocess.py输出的tensor形状和数值,与训练时debug日志比对
PDF报告中文乱码Thymeleaf模板未指定UTF-8或IText7字体缺失docker exec -it dermatology-springboot ls /app/fonts/将simhei.ttf字体文件放入springboot_backend/src/main/resources/static/fonts/,并在PDF生成代码中指定字体

独家避坑技巧:
-Windows路径问题:若在Windows上用Git Bash执行docker-compose up,确保docker-compose.yml中所有路径使用正斜杠/,避免反斜杠\被转义。资源包中已统一使用/
-模型文件权限:ONNX模型文件在Linux容器中需有读权限。若docker build后模型无法加载,进入容器执行ls -l model/,若显示-rw-------,则在宿主机执行chmod 644 model/*.onnx再重新build。
-Nginx缓存干扰:开发调试时,浏览器可能缓存旧JS。在vue_app/vite.config.js中添加:
js export default defineConfig({ server: { host: '0.0.0.0', port: 5173, hmr: { overlay: false } }, build: { rollupOptions: { output: { manualChunks: { vendor: ['vue', 'axios', 'element-plus'] } } } } })
并在Nginx配置中禁用静态资源缓存:
nginx location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires off; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; }

5. 模型替换与系统扩展实战:如何接入自己的数据集或升级模型

5.1 替换ISIC2019模型为自定义模型的五步法

假设你收集了1000张某三甲医院的银屑病皮肤镜图像,想替换掉默认的ISIC2019模型。这不是简单替换.onnx文件,而是完整的模型生命周期管理:

步骤1:数据准备与标注
- 图像格式:统一为JPEG,分辨率缩放至1024×768(保持长宽比,短边填充黑边)
- 标注规范:按ISIC2019的7类体系,新增PSO(银屑病)类,其他类保留。使用LabelImg工具生成Pascal VOC格式XML,再用脚本转为CSV:
filename,class,xmin,ymin,xmax,ymax psoriasis_001.jpg,PSO,120,80,320,280

步骤2:训练新模型
- 使用资源包中train/目录下的train_efficientnet.py(已预置ISIC2019训练脚本)
- 修改数据路径:
python train_df = pd.read_csv('./data/custom_train.csv') val_df = pd.read_csv('./data/custom_val.csv')
- 修改类别数:num_classes = 8(原7类+PSO)
- 关键超参调整:因数据量少,lr=1e-4batch_size=16epochs=50,启用早停(patience=5)

步骤3:导出ONNX模型
训练完成后,加载最佳权重:

model = EfficientNet.from_pretrained('efficientnet-b3', num_classes=8) model.load_state_dict(torch.load('best_model.pth')) model.eval() dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, "model/custom_pso.onnx", input_names=["input"], output_names=["output"], dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, opset_version=12 )

步骤4:更新Flask服务
- 将custom_pso.onnx放入onnx_app/model/,覆盖原文件
- 修改onnx_app/app.pyclass_names列表:
python class_names = ['MEL', 'NV', 'BCC', 'AKIEC', 'BKL', 'DF', 'VASC', 'PSO']
- 重建Flask镜像:cd onnx_app && docker build -t dermatology-flask .

步骤5:更新前端与后端映射
- Vue前端:修改src/api/flask.jsCLASS_COLORS对象,为PSO添加专属颜色(如紫色#8A2BE2
- SpringBoot:在CaseRecord.java中增加psoriasisRiskLevel字段,用于存储银屑病严重程度评估(可后续接入临床评分量表)

注意:模型替换后,必须同步更新util/preprocess.py中的归一化参数。若新数据集光照差异大,需重新计算mean/std:
```python

计算自定义数据集均值标准差

mean = np.array([0.492, 0.465, 0.438]) # 示例值,需实际计算
std = np.array([0.231, 0.226, 0.224])
```

5.2 系统能力扩展:从单图诊断到批量分析与API开放

当前系统聚焦单图交互,但临床真实需求常是批量处理。扩展方案如下:

批量诊断功能(SpringBoot侧)
- 新增Controller端点:POST /api/v1/batch/predict
- 接收ZIP文件,解压后遍历所有图片,异步调用Flask的/api/v1/predict(用@Async注解)
- 结果汇总为Excel,包含列:文件名、主诊断、概率、次诊断、推理时间、备注
- 前端增加“批量上传ZIP”按钮,进度条显示已完成/总数

对外API开放(安全可控)
- 当前API无鉴权,仅限内网。如需对外提供,启用Spring Security:
java @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeHttpRequests(authz -> authz .requestMatchers("/api/v1/predict").authenticated() .anyRequest().permitAll() ) .httpBasic(); // 简单HTTP Basic认证 return http.build(); } }
- 在application.yml中配置API Key白名单:
yaml api: keys: - name: hospital_a key: abc123def456 - name: research_lab key: xyz789uvw012

模型监控与反馈闭环
- 在Flask中增加/api/v1/metrics端点,返回:
- 请求总数、成功率、平均延迟
- 各类别预测分布(直方图数据)
- 前端“历史记录”页增加“反馈此结果”按钮,提交医生修正标签,存入feedback_table,用于后续模型迭代。

我个人在实际部署中发现,最实用的扩展不是炫技的功能,而是日志审计。我们在SpringBoot中增加了@Aspect切面,记录所有/api/v1/predict调用的完整请求体(脱敏后)、响应体、耗时、调用IP,日志格式为JSON,可直接对接ELK栈。这样当医生质疑某次诊断时,运维能秒级定位是模型问题、预处理问题还是网络抖动问题——这才是医疗AI落地的信任基石。

这套系统没有魔法,它只是把每一个环节——从像素矩阵的数值变换,到HTTP请求的字节流,再到MySQL事务的ACID保证——都掰开揉碎,用最朴实的代码和最详尽的文档,铺成一条可触摸、可验证、可扩展的路径。当你第一次看到“MEL: 0.92”出现在浏览器里,那不是AI的胜利,而是工程确定性的胜利。

本文还有配套的精品资源,点击获取

简介:直接可运行的皮肤病图像识别系统,前端用Vue.js搭建交互界面,支持图片上传、实时结果显示和诊断报告查看;后端分两层:Flask服务加载ONNX格式深度学习模型(基于ISIC2019数据集训练),专注图像预处理与皮肤病变分类(如黑色素痣、基底细胞癌、血管病变、良性角化病等);SpringBoot服务负责用户登录、病例存档、报告生成及MySQL数据管理。所有模块均提供标准化Dockerfile和docker-compose.yml,一条命令启动全部服务(Vue+Nginx、Flask、SpringBoot、MySQL)。资源包内置真实测试图例、训练好的ONNX模型文件、前后端完整源码、数据库初始化SQL脚本、API接口文档、Nginx配置示例、Docker部署说明和详细开发指南,适配高校课程设计、毕设开发或医疗AI原型快速验证。本地已实测通过多轮功能验证,无需额外配置即可启动使用,后续可灵活替换模型或接入新数据源。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 16:28:30

BGP工作原理:邻居关系、路由通告与注入机制详解

一、BGP基础概述边界网关协议&#xff08;BGP&#xff09;是一种主要工作在自治系统&#xff08;AS&#xff09;之间的动态路由协议&#xff0c;其核心功能是为AS间提供无环路的路由信息交互。作为互联网的核心路由协议&#xff0c;BGP的设计目标是在不同自治系统之间建立可靠的…

作者头像 李华
网站建设 2026/6/8 16:28:13

基于MC68HC908MR24的三相异步电机V/Hz闭环调速系统设计详解

1. 项目概述与核心价值如果你正在寻找一个用经典8位单片机实现工业级电机控制的完整参考方案&#xff0c;那么这篇基于MC68HC908MR24的三相异步电机闭环调速系统设计&#xff0c;绝对值得你花时间仔细研究。这个项目源自飞思卡尔&#xff08;现恩智浦&#xff09;的一份经典应用…

作者头像 李华
网站建设 2026/6/8 16:25:42

不会谈薪,真的会把自己谈成“最低价”

很多同学找工作&#xff0c;最紧张的不是投简历&#xff0c;也不是等面试结果。而是好不容易走到最后&#xff0c;HR 问了一句&#xff1a;“你的期望薪资是多少&#xff1f;”这句话一出来&#xff0c;很多人就开始慌了。说高了&#xff0c;怕 offer 飞了。 说低了&#xff0c…

作者头像 李华
网站建设 2026/6/8 16:20:23

免费AI视频增强终极指南:用Video2X轻松提升视频画质

免费AI视频增强终极指南&#xff1a;用Video2X轻松提升视频画质 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/video2x…

作者头像 李华
网站建设 2026/6/8 16:18:56

拒绝答辩 PPT 内耗!paperxie AI PPT,让你的毕业成果自带高光

paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/AI PPTAI PPT制作 - PaperXie智能写作PaperXie免费论文查重检测-首款免费论文检测软件,为毕业生提供专业的论文重复率检测、论文降重、Aigc检测、智能排版 、论文写作等一站式服务。https://www.paperxie.c…

作者头像 李华