Fun-ASR-MLT-Nano-2512语音识别案例:医疗问诊录音→结构化病历生成

1. 引言

想象一下这个场景:一位医生刚结束了一上午的门诊,面对堆积如山的问诊录音,需要手动整理成电子病历。这个过程不仅耗时费力,还容易因为听错或漏记关键信息而影响后续诊疗。这几乎是每位临床医生都会遇到的痛点。

今天,我们要聊的就是如何用技术来解决这个具体问题。借助阿里通义实验室开源的 Fun-ASR-MLT-Nano-2512 语音识别模型,我们可以将医生与患者的对话录音,自动、准确地转换成文字,并进一步处理成结构化的电子病历。这不仅仅是把声音变成文字,更是将非结构化的对话,转化为可以直接录入医院信息系统的标准格式。

这个方案的核心价值非常直接:为医生减负,为诊疗提效。通过自动化处理问诊录音,医生可以节省大量用于文书工作的时间,将精力更多地投入到患者身上。同时,标准化的病历结构也有助于提升医疗数据的质量和后续分析的便利性。

在接下来的内容里,我不会只讲模型有多厉害,而是会带你一步步走完从部署模型到实际生成病历的完整流程。你会看到具体的代码、遇到的实际问题以及解决方法,最终实现一个能真正跑起来的“录音转病历”小工具。

2. 项目与环境准备

2.1 理解我们的工具:Fun-ASR-MLT-Nano-2512

在动手之前,我们先快速了解一下将要使用的核心工具。

Fun-ASR-MLT-Nano-2512 是阿里通义实验室推出的一个多语言语音识别模型。名字有点长,我们拆开看:

  • Fun-ASR:代表了“Fun Audio”的语音识别框架。
  • MLT:意思是“Multi-Lingual Transcription”,支持多语言转录。
  • Nano:说明这是一个轻量级的版本,对硬件要求相对友好。
  • 2512:可能指代模型的版本或发布日期标识。

这个模型有几个对医疗场景特别有用的特点:

  1. 支持31种语言:除了普通话,还能识别英语、粤语等,适合多方言地区的医院。
  2. 高噪声环境识别:官方数据显示在远场高噪声环境下仍有93%的准确率,这很重要,因为诊室环境并不总是安静的。
  3. 轻量化:模型大小约2GB,相比动辄几十GB的大模型,部署起来要轻松得多。
  4. 开箱即用:提供了基于Gradio的Web界面和Python API,方便我们快速集成。

2.2 搭建你的工作环境

为了让项目顺利运行,你需要准备一个Linux环境。如果你用的是Windows,建议使用WSL2(Windows Subsystem for Linux)或者直接在云服务器上操作。

基础要求

  • 操作系统:Ubuntu 20.04 或更高版本(其他Linux发行版也可,但以下命令以Ubuntu为例)。
  • Python:版本3.8或以上。
  • 内存:至少8GB。
  • 磁盘空间:预留5GB以上空间用于存放模型和依赖。
  • GPU(可选但推荐):如果你有支持CUDA的NVIDIA显卡,推理速度会快很多。没有GPU也能用,只是慢一些。

首先,我们通过SSH连接到你的Linux服务器或打开终端,创建一个专门的项目目录并进入:

mkdir -p ~/projects/medical_asr
cd ~/projects/medical_asr

接下来,获取项目代码。模型已经由社区开发者“by113小贝”进行了二次开发并封装成了可直接部署的镜像,我们可以直接使用:

# 克隆项目仓库(这里假设代码已托管在GitHub,请替换为实际仓库地址)
git clone <项目仓库地址> .
# 或者,如果你已经下载了压缩包,直接解压到当前目录
# unzip Fun-ASR-MLT-Nano-2512.zip -d .

进入项目目录,你会看到类似下面的结构:

Fun-ASR-MLT-Nano-2512/
├── app.py                    # 基于Gradio的Web服务主文件
├── model.py                  # 模型定义文件(内含重要Bug修复)
├── requirements.txt          # Python依赖包列表
├── model.pt                  # 预训练模型权重文件(约2GB)
└── example/                  # 示例音频文件,用于测试

2.3 安装依赖与启动服务

环境准备好了,我们开始安装运行所需的软件包。

第一步:安装系统依赖 主要是音频处理工具FFmpeg,模型用它来读取各种格式的音频文件。

sudo apt-get update
sudo apt-get install -y ffmpeg

第二步:安装Python依赖 建议使用Python虚拟环境来管理依赖,避免污染系统环境。

# 创建虚拟环境(如果你还没有安装venv,先运行 sudo apt-get install -y python3-venv)
python3 -m venv venv

# 激活虚拟环境
source venv/bin/activate

# 安装项目所需的Python包
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

requirements.txt 里通常包含了 torch(深度学习框架)、gradio(Web界面库)、funasr(语音识别库)等。

第三步:启动语音识别Web服务 一切就绪,现在可以启动服务了。我们使用 nohup 让服务在后台运行,并把日志输出到文件。

cd /root/Fun-ASR-MLT-Nano-2512  # 请确保路径正确
nohup python app.py > /tmp/funasr_web.log 2>&1 &
echo $! > /tmp/funasr_web.pid

这行命令做了三件事:

  1. nohup ... &:在后台运行 app.py
  2. > /tmp/funasr_web.log 2>&1:把程序的标准输出和错误输出都重定向到日志文件,方便排查问题。
  3. echo $! > /tmp/funasr_web.pid:把后台进程的ID号保存到文件,之后用来停止服务。

第四步:访问服务 服务启动后,默认会在本机的7860端口监听。打开你的浏览器,访问:

http://你的服务器IP地址:7860

如果一切正常,你会看到一个简洁的网页界面,可以上传音频文件或直接录音进行识别测试。先用 example/ 目录下的示例音频试试看吧!

3. 从语音到文本:核心识别功能实战

服务跑起来了,但我们的目标不只是做一个语音识别的演示。我们需要的是一个能集成到病历生成流程中的可靠组件。这意味着我们要绕过Web界面,直接使用Python代码来调用模型的识别能力。

3.1 修复一个关键Bug

在直接使用代码前,有一个重要的问题需要先解决。社区开发者发现项目自带的 model.py 文件里有一个Bug,会导致在某些情况下推理失败。问题出在第368行到406行左右的一个异常处理逻辑里。

简单来说,代码原本的设计是:尝试加载音频,如果出错就记录日志,但之后仍然使用了可能未成功加载的变量,导致程序崩溃。

修复方法很简单,我们把处理音频的核心逻辑移到 try 语句块内部,确保只有加载成功后才进行后续处理。如果你下载的代码已经修复,可以跳过这一步。如果没有,你需要打开 model.py 文件,找到相关代码段进行调整。

修复后的代码逻辑示意

# 正确的逻辑:在try块内完成关键操作
try:
    # 1. 加载音频文件
    data_src = load_audio_text_image_video(audio_file_path, ...)
    # 2. 提取音频特征(只有上一步成功,data_src才有效)
    speech, speech_lengths = extract_fbank(data_src, ...)
    # 3. 其他后续处理...
    # ... [其他处理逻辑]
except Exception as e:
    # 如果上面任何一步出错,记录日志并跳过当前文件,处理下一个
    logging.error(f"处理文件 {audio_file_path} 时出错: {e}")
    continue  # 跳过这个文件,继续处理下一个

这个修复保证了程序的健壮性,即使某个音频文件损坏或格式不支持,也不会导致整个任务中断。

3.2 编写你的语音识别脚本

现在,我们来编写一个实用的Python脚本,它能够接收一个问诊录音文件,并输出识别后的文字。

创建一个新文件,比如叫 transcribe_medical.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
医疗问诊录音转文字脚本
"""

import sys
import os
from funasr import AutoModel

def transcribe_audio(audio_path, model_dir=".", language="中文"):
    """
    将音频文件转换为文字
    Args:
        audio_path: 音频文件路径
        model_dir: 模型所在目录路径
        language: 识别语言,默认为中文
    Returns:
        识别出的文本字符串
    """
    print(f"正在加载模型,模型目录: {model_dir}...")
    # 初始化模型,指定模型路径,并信任远程代码(因为用了自定义模型文件)
    model = AutoModel(
        model=model_dir,
        trust_remote_code=True,
        device="cuda:0"  # 如果有GPU,使用GPU加速。如果没有,改为 device="cpu"
    )
    print("模型加载完毕,开始识别...")

    # 执行识别
    # 注意:input参数需要是一个列表,即使只有一个文件
    result = model.generate(
        input=[audio_path],
        cache={},
        batch_size=1,  # 一次处理一个文件
        language=language,
        itn=True  # 启用逆文本归一化,将“一二三”转为“123”等
    )

    # 提取识别结果
    # result 是一个列表,里面是字典,文本在 "text" 字段中
    if result and len(result) > 0:
        transcribed_text = result[0].get("text", "")
        return transcribed_text
    else:
        return "识别失败,未返回有效结果。"

if __name__ == "__main__":
    # 使用示例
    if len(sys.argv) < 2:
        print("用法: python transcribe_medical.py <音频文件路径> [语言,默认为'中文']")
        sys.exit(1)

    audio_file = sys.argv[1]
    lang = sys.argv[2] if len(sys.argv) > 2 else "中文"

    if not os.path.exists(audio_file):
        print(f"错误:文件 '{audio_file}' 不存在。")
        sys.exit(1)

    text = transcribe_audio(audio_file, language=lang)
    print("\n" + "="*50)
    print("识别结果:")
    print("="*50)
    print(text)

如何使用这个脚本

  1. 将上面的代码保存为 transcribe_medical.py
  2. 在终端中,确保你的虚拟环境是激活的 (source venv/bin/activate)。
  3. 运行脚本:
    python transcribe_audio.py /path/to/your/medical_recording.mp3
    
  4. 如果录音是粤语的,可以指定语言:
    python transcribe_audio.py /path/to/recording.mp3 粤语
    

运行后,你会看到模型加载的提示(首次加载可能需要几十秒),然后输出识别出的文字。至此,我们已经完成了从录音到文字的关键一步。

4. 从文本到病历:结构化信息提取

拿到大段的对话文本只是第一步。医生问诊录音通常是自由对话,包含寒暄、重复、语气词等。我们需要从中提取出关键信息,并组织成结构化的病历格式,比如主诉、现病史、既往史、诊断意见等。

4.1 定义病历结构

首先,我们要明确一份简化的门诊病历包含哪些部分。这里我们定义一个Python字典来代表结构:

# 定义一个病历模板结构
MEDICAL_RECORD_TEMPLATE = {
    "就诊时间": "",
    "患者信息": {
        "姓名": "",
        "性别": "",
        "年龄": "",
        "就诊科室": ""
    },
    "主诉": "",          # 患者最主要的痛苦或就诊原因,包括症状及持续时间
    "现病史": "",        # 本次疾病的发生、演变、诊疗等全过程
    "既往史": "",        # 患者过去的健康和疾病情况
    "体格检查": "",      # 本次就诊的检查结果(录音中可能不全)
    "初步诊断": "",      # 医生根据当前信息做出的诊断
    "治疗意见": "",      # 建议的治疗方案或用药
    "医师签名": ""
}

4.2 使用大语言模型进行信息提取与结构化

单纯依靠规则从自由文本中提取这些信息非常困难。这时,我们可以借助另一个强大的工具——大语言模型(LLM),比如通过API调用诸如GPT-4、文心一言、通义千问等模型,来理解文本并按要求格式化。

下面是一个示例函数,它接收语音识别后的文本,调用LLM API,输出结构化的病历JSON。这里以使用OpenAI格式的API为例(你需要有自己的API Key):

import json
import requests

def extract_medical_info_with_llm(transcribed_text, api_key, api_base="https://api.openai.com/v1"):
    """
    使用大语言模型从问诊对话文本中提取结构化病历信息。
    """
    # 构建一个清晰的提示词(Prompt),告诉模型我们要做什么
    prompt = f"""
你是一名经验丰富的医疗文书助理。请将以下医患问诊对话记录,整理成一份结构化的门诊电子病历。
请严格按照指定的JSON格式输出,只输出JSON对象,不要有任何额外的解释或标记。

【对话记录开始】
{transcribed_text}
【对话记录结束】

请提取信息并填充到以下JSON结构中:
{json.dumps(MEDICAL_RECORD_TEMPLATE, ensure_ascii=False, indent=2)}

提取要求:
1. “就诊时间”若对话中未明确,请填写“未提及”。
2. “患者信息”部分,尽可能从对话中推断,无法推断的字段留空。
3. “主诉”需精简概括患者最主要的症状和持续时间。
4. “现病史”需按时间顺序梳理疾病发展过程。
5. 对话中未涉及的部分(如某些检查结果),对应字段留空或写“未涉及”。
6. 所有内容使用中文。
"""
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    data = {
        "model": "gpt-3.5-turbo",  # 或你使用的其他模型
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.1,  # 温度调低,使输出更稳定、更格式化
        "response_format": {"type": "json_object"}  # 要求返回JSON
    }

    try:
        response = requests.post(f"{api_base}/chat/completions", headers=headers, json=data, timeout=60)
        response.raise_for_status()
        result = response.json()
        llm_output = result["choices"][0]["message"]["content"]

        # 解析模型返回的JSON
        structured_record = json.loads(llm_output)
        return structured_record
    except requests.exceptions.RequestException as e:
        print(f"API请求失败: {e}")
        return None
    except json.JSONDecodeError as e:
        print(f"解析模型返回的JSON失败: {e}")
        print(f"原始返回内容: {llm_output}")
        return None

几点说明

  1. 提示词(Prompt)是关键:我们通过详细的指令,让模型扮演“医疗文书助理”的角色,并给出了明确的输出格式和要求。这能极大提高信息提取的准确性。
  2. 备用方案:如果你没有可用的LLM API,也可以考虑使用本地的、轻量化的开源模型(如Qwen、ChatGLM等),通过Ollama、LM Studio等工具部署后调用,原理类似。
  3. 隐私与安全:处理真实的医疗录音时,务必注意数据隐私。确保API调用符合数据安全规范,或使用本地部署的模型。

4.3 整合流程:端到端的病历生成

现在,我们把语音识别和信息提取两个步骤串联起来,形成一个完整的自动化流水线。

创建一个主程序文件 generate_medical_record.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
医疗问诊录音自动生成结构化病历 - 主程序
"""

import sys
import os
import json
from datetime import datetime
from transcribe_medical import transcribe_audio  # 导入我们之前写的识别函数
# 假设 extract_medical_info_with_llm 函数在另一个文件 llm_extractor.py 中
from llm_extractor import extract_medical_info_with_llm

def main(audio_file_path, llm_api_key, output_dir="./output"):
    """
    主流程:音频文件 -> 文本 -> 结构化病历
    """
    print("="*60)
    print("开始处理医疗问诊录音...")
    print(f"音频文件: {audio_file_path}")
    print("="*60)

    # 步骤1:语音识别
    print("\n[步骤1/2] 正在进行语音识别...")
    try:
        transcribed_text = transcribe_audio(audio_file_path)
        print("语音识别完成。")
        # 可选:保存原始文本
        raw_text_path = os.path.join(output_dir, "raw_transcription.txt")
        with open(raw_text_path, 'w', encoding='utf-8') as f:
            f.write(transcribed_text)
        print(f"原始文本已保存至: {raw_text_path}")
    except Exception as e:
        print(f"语音识别失败: {e}")
        return

    # 步骤2:信息提取与结构化
    print("\n[步骤2/2] 正在提取信息并生成结构化病历...")
    try:
        structured_record = extract_medical_info_with_llm(transcribed_text, llm_api_key)

        if structured_record:
            # 添加处理时间戳
            structured_record["处理信息"] = {
                "音频文件": os.path.basename(audio_file_path),
                "处理时间": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "模型版本": "Fun-ASR-MLT-Nano-2512"
            }

            # 保存结构化病历为JSON文件
            os.makedirs(output_dir, exist_ok=True)
            base_name = os.path.splitext(os.path.basename(audio_file_path))[0]
            json_output_path = os.path.join(output_dir, f"{base_name}_medical_record.json")

            with open(json_output_path, 'w', encoding='utf-8') as f:
                json.dump(structured_record, f, ensure_ascii=False, indent=2)

            print("\n" + "="*60)
            print("结构化病历生成成功!")
            print("="*60)
            print(f"病历已保存至: {json_output_path}")
            print("\n病历内容预览:")
            print(json.dumps(structured_record, ensure_ascii=False, indent=2)[:500] + "...") # 预览前500字符
        else:
            print("信息提取失败,未能生成结构化病历。")

    except Exception as e:
        print(f"信息提取过程失败: {e}")
        return

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("用法: python generate_medical_record.py <音频文件路径> <LLM_API_KEY>")
        print("示例: python generate_medical_record.py ./clinic_recording.mp3 sk-...your_api_key")
        sys.exit(1)

    audio_path = sys.argv[1]
    api_key = sys.argv[2]

    if not os.path.exists(audio_path):
        print(f"错误:音频文件 '{audio_path}' 不存在。")
        sys.exit(1)

    # 创建输出目录
    output_directory = "./medical_records_output"
    main(audio_path, api_key, output_directory)

运行这个脚本,你只需要提供音频文件路径和你的LLM API Key。程序会自动执行以下流程:

  1. 调用Fun-ASR模型将录音转为文字。
  2. 调用大语言模型,从文字中提取关键信息并填充到病历模板中。
  3. 将最终的结构化病历保存为一个清晰的JSON文件。

5. 总结与展望

通过上面的步骤,我们完成了一个从医疗问诊录音自动生成结构化病历的完整原型。让我们回顾一下关键点:

核心流程回顾

  1. 环境部署:我们成功部署了Fun-ASR-MLT-Nano-2512语音识别模型,它是一个支持多语言、轻量且准确度不错的工具。
  2. 语音转文本:通过编写Python脚本,我们能够稳定地调用模型,将各种格式的音频文件转换为文字记录,并修复了可能遇到的一个关键Bug。
  3. 文本结构化:这是将技术转化为实用价值的关键一步。我们利用大语言模型强大的自然语言理解能力,设计提示词,将杂乱的对话文本整理成标准、结构化的病历JSON。

这个方案的价值

  • 提升效率:将医生从繁琐的文书录入工作中解放出来。
  • 规范病历:自动生成的结构化数据更利于后续的统计分析、科研和医保结算。
  • 降低差错:减少因听错、漏记导致的人为错误。

可以继续优化的方向

  • 本地化部署:将大语言模型也本地化部署,彻底避免数据上云带来的隐私顾虑。
  • 专业词库优化:针对医疗领域专有名词(药品名、疾病名、手术名)对语音识别模型进行微调,进一步提升转写准确率。
  • 流程集成:将本工具与医院的HIS(医院信息系统)或电子病历系统对接,实现录音自动上传、识别、结构化并填充到病历表单中的全自动化流程。
  • 多模态输入:未来可以结合诊间的视频记录,不仅识别语音,还能分析医生的手势、指向的检查影像等,生成更全面的病历。

技术的目的始终是服务于人。这个案例展示了如何将先进的AI模型与具体的行业场景(医疗)相结合,解决一个真实存在的痛点。希望这个从0到1的构建过程,能给你带来一些启发。你可以基于这个原型,根据自己的具体需求进行修改和扩展,打造出更贴合实际应用场景的工具。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐