十一月的广州,终于有了一丝秋意。宿舍楼下的流浪猫不再躲在空调外机下面喘气,晚风吹进阳台的时候,带着点潮湿的凉。大四的课表已经空得差不多了,室友们大多在准备考公或者在外面实习,寝室里常常只有我一个人。
机械键盘的敲击声在安静的房间里显得特别清晰。屏幕右下角的时间跳到凌晨两点半,终端里最后一行 webpack compiled successfully 亮起绿色的字体。我靠在椅背上,长长地呼出一口气。
这是 Oculichat 跑通全流程的那个夜晚。
Oculichat 是我最近几个月几乎全部生活的重心。简单来说,它是一个眼底图像的 AI 辅助诊断与问诊系统。如果在一年前,有人告诉我,我一个在广州商学院读软件工程的本科生,会去碰医疗影像、去啃眼科病理学的全英文论文,我大概会觉得他在开玩笑。医疗,这是一个听起来就带着消毒水味、严谨到让人不敢轻易涉足的领域。
但事情的起因其实很偶然。大三下学期,我在构思一个比赛项目时,偶然看到了一篇关于基层医疗现状的报道。文章里提到,眼底检查是诊断青光眼、糖尿病视网膜病变(DR)、黄斑变性等致盲性疾病的“金标准”。因为视网膜是人体唯一能直接观察到微血管的部位,它就像一面镜子,不仅能反映眼部疾病,甚至能折射出心血管和内分泌系统的早期异常。
然而现实是,专业的眼底读片医生极度匮乏。在很多乡镇卫生院,即便配备了眼底相机,拍出来的片子也没有人能准确解读。病患往往需要拿着片子,坐几个小时的大巴去市里的三甲医院排队。而像糖尿病视网膜病变这样的疾病,早期几乎没有痛感,视力下降是一个缓慢渐进的过程。等患者自己觉得“看不清了”再去医院,往往已经到了晚期,造成了不可逆的视力损伤。
那一刻,我突然觉得,代码或许可以做点什么。不是那种为了炫技而写的花哨应用,而是能在某个偏远诊所的旧电脑上,静静运转、替疲惫的医生分担一点压力的工具。
开发的第一步,是从理解数据开始的。我以为处理医学图像和以前做过的猫狗图像分类差不多,但当我从公开数据集(比如 APTOS 和 EyePACS)上下载了几十个 G 的眼底图片后,我才意识到事情没那么简单。
广商的校园网在深夜依然有些限速,进度条爬得很慢。等数据解压出来,我看着屏幕上几千张眼底照片,有些发愣。那是一个个真实的、带着血丝和神经纤维的视网膜。有的干净平滑,呈现出健康的橘红色;有的却布满了黄色的硬性渗出物、暗红色的微血管瘤,甚至是大片的出血斑。
这些在医生眼里特征鲜明的病灶,在计算机看来,只是一堆像素矩阵。而且,真实世界的数据太脏了。有的图片曝光过度,像是在强光下拍的;有的边缘被严重裁剪,视野极窄;还有的因为患者眼球转动,糊成了一团。
我花了整整一个星期的时间写 Python 脚本做数据预处理。用 OpenCV 裁剪掉多余的黑边,做直方图均衡化来统一亮度和对比度,再用高斯滤波去除噪点。每天看着屏幕上成百上千只“眼睛”闪过,有时候半夜去洗手间,看着镜子里的自己,都会下意识地盯着眼球上的红血丝看。
在技术选型上,前端我用了 TypeScript + React。我一直很偏爱 TypeScript,它那种严格的类型约束能给我一种安全感,尤其是在处理复杂的医疗数据结构时,定义好每一个 interface,就像是在盖房子前打好了地基。UI 设计上,我克制了使用过多动画和鲜艳色彩的冲动。考虑到医生的工作场景,界面必须干净、克制、信息层级分明。左边是图像上传和预览区,右边是结构化的诊断报告,没有任何多余的视觉干扰。
而后端和 AI 模型的对接,是整个项目最难啃的骨头。
一开始,我想过直接套用当时最火的 ViT(Vision Transformer)模型。但跑了几次实验后,我发现 ViT 对算力的要求太高,而且在中小规模的数据集上,它的表现并不比传统的 CNN 好多少。我的开发环境只是一台带着普通消费级显卡的电脑,没有实验室那种动辄几张 A100 的豪华配置。
现实逼着我做减法。最终,我选择了基于 ResNet50 作为主干网络。这是一个经过时间检验的经典模型,参数量适中,特征提取能力稳定。但仅仅分类出“有病”或“没病”是不够的。医疗 AI 最大的痛点在于“黑盒效应”——医生需要知道,AI 凭什么得出这个结论。
为了解决可解释性的问题,我在 ResNet 的基础上引入了 Attention 机制,并结合 Grad-CAM(梯度类激活映射)技术。这样,模型在输出诊断结果的同时,还能生成一张热力图。
我清楚地记得,当模型第一次成功输出热力图的那个下午。屏幕上,一张患有严重糖尿病视网膜病变的眼底图上,AI 用深红色精准地高亮了视网膜下方的几处微血管瘤和渗出区域。那一刻,我没有欢呼,只是静静地看着屏幕。那种感觉很奇妙,就像是你教会了一个原本盲目的机器,让它拥有了凝视人类苦难的能力。
但这还不够。在系统基本成型后,我又加了一个功能:患者问诊对话(ChatBot)。
这个想法来源于我陪家人去医院看病的经历。每次拿到体检报告,面对上面密密麻麻的专业术语——“杯盘比扩大”、“黄斑区水肿”、“豹纹状眼底”——普通人的第一反应往往是恐慌。医生太忙了,没有时间给每一个患者详细解释这些词汇背后的含义。
所以我接入了 LLM(大语言模型)的推理服务,做了一个医生端管理后台与患者端的隔离。在患者端,诊断报告生成后,Oculichat 会用一种温和、通俗的口吻,将冰冷的医学术语翻译成普通人能听懂的大白话。
“你的眼底照片显示,黄斑区有一些小点点的渗出。这就像是相机的底片上落了一点灰尘,目前不需要太紧张,但平时要注意控制血糖,少熬夜,建议下周去眼科做个复查。”
当我在测试框里输入模拟的病理特征,看着 ChatBot 一字一句地吐出这段话时,我突然觉得,技术并不是冷冰冰的。它可以有温度,可以成为连接专业知识与普通人焦虑之间的一座桥梁。
带着这个项目,我报名参加了省里的计算机设计大赛。
答辩那天,我坐在候场区,看着周围来自中山大学、华南理工等 985/211 高校的队伍。他们的 PPT 做得极其精美,有的项目甚至带来了全套的物联网硬件设备,在台上闪闪发光。作为一所民办三本院校的学生,说心里完全没有落差是假的。那是一种长期萦绕在潜意识里的自我怀疑:我的东西,真的拿得出手吗?
轮到我上台时,我没有讲太多宏大的概念,也没有用夸张的词汇去包装。我只是打开系统,上传了一张真实的眼底照片,让评委看着进度条走完,看着热力图生成,看着 ChatBot 给出温和的建议。
“我的初衷很简单,”在 Q&A 环节,面对评委关于商业模式的提问,我握着麦克风,声音有点发紧,但很平稳,“我只是希望,在那些没有三甲医院的乡镇,当一个老人觉得眼睛模糊时,村里的医生能用这个工具告诉他,是该立刻去市里做手术,还是只需要滴点眼药水。”
台下安静了几秒。那个下午的阳光透过会议室的百叶窗打在投影幕布上,我看着自己写的 UI 界面,突然觉得,学校的标签、GPA 4.02 的成绩单、甚至是双国奖的荣誉,在这一刻都不重要了。代码是诚实的,它运行起来的每一行逻辑,都在替我表达我真正想说的话。
比赛的结果还不错,拿到