通过python的transfermers库,使用本地部署的chatglm-6B大语言模型,实现根据患者自述自动生成病历功能,输入为病人自述,输出为json格式的病历结果。
功能定位:患者来医院挂号获取病历时,先简单向系统描述自身病情,系统从患者自述中提取病历相应的条目,自动填写到病历相应位置,生成病历,并传到医生端,供医生查看病人的基本病历信息。
输出的JSON对象包含以下键:
- 姓名:病人的名字
- 年龄:病人的年龄
- 主诉:病人主要的症状
- 持续时间:症状持续的时间
- 既往病史:病人的历史疾病
- 过敏药物:病人对哪些药物过敏
如果某个信息在文本中没有提及,则使用['未提供']
表示。
输入示例:'我叫林凡,今年42岁,从3天前开始一直觉得肩膀酸痛,几十年前曾经得过肩周炎,但是已经痊愈,没有发现药物过敏'
输出示例:{'姓名': '林凡', '年龄': '42岁', '主诉': '肩膀酸痛', '持续时间': '3天', '既往病史': '肩周炎', '过敏药物': '未提供'}
将输出的json数据传给前端,填写到病历的相应位置中。
1.导入必要的库:
re
:正则表达式库,用于文本处理。json
:处理JSON数据格式。transformers
:提供预训练模型和自然语言处理工具。
2.定义数据结构和示例,用于后续prompt:
schema
:定义了需要从患者自述中提取的信息种类,这里是['姓名','年龄','主诉','持续时间','既往病史','过敏药物']。IE_PATTERN
:用于生成提示信息的模式字符串,它指导模型如何进行信息抽取。ie_examples
:包含具体的示例病历,以及从这些病历中预期抽取的正确信息。
3.生成提示信息:
init_prompts()
:- 创建了一个列表
ie_prefix
,其中包含了初始化信息抽取任务的描述和每个示例病历的具体提示。这个字符串用于生成训练或测试信息抽取模型的提示,明确告诉模型需要从文本中抽取哪些具体的信息类型,并在接收前端用户输入后与用户输入拼接,生成带prompt的输入,传给大模型进行处理。
properties_str = ', '.join(schema['病历条目'])
这行代码执行了以下操作:
-
访问字典:
schema
是一个字典,其中包含键'病历条目'
。这个键关联到一个列表,列表包含多个字符串:['姓名', '年龄', '主诉', '持续时间', '既往病史', '过敏药物']
。 -
使用
join()
方法:join()
方法是一个字符串方法,它接受一个可迭代的参数(在这个例子中是列表),然后将列表中的每个元素(这里是字符串)连接成一个单一的字符串。连接时,元素之间会插入调用join()
方法的字符串,这里是', '
。因此,列表中的每个元素之间会被逗号和一个空格分隔。 -
结果:
properties_str
变量将包含以下字符串:'姓名, 年龄, 主诉, 持续时间, 既往病史, 过敏药物'
。
schema_str_list = f'“病历条目”({properties_str})'
利用 Python 的 f-string(格式化字符串)功能构造一个包含前面生成的 properties_str
字符串的新字符串。
- f-string: 使用前缀
f
表示这是一个格式化字符串,允许在花括号{}
中直接插入并执行 Python 表达式。 - 花括号
{}
中的内容: 这里的{properties_str}
会被替换为变量properties_str
的值,即'姓名, 年龄, 主诉, 持续时间, 既往病史, 过敏药物'
。 - 括号和引号: 字符串外围的
“病历条目”
和圆括号()
用于文本格式化,使最终的字符串在视觉上更清晰,标示出这是一组特定的信息类型,其中“病历条目”
可能是用于提醒或强调这是一组特定的医疗记录字段。
结果
执行这行代码后,schema_str_list
变量将包含字符串:
“病历条目”(姓名, 年龄, 主诉, 持续时间, 既往病史, 过敏药物)
这个格式化的字符串在后续的代码中可能用于构建更复杂的提示或消息,特别是在需要明确指出需要从文本中提取哪些具体字段时。在信息抽取或自然语言处理的任务中,清晰地标示出目标信息类型对于确保模型输出的准确性至关重要。
这段代码遍历 ie_examples
字典中的所有条目,为每个条目生成特定的训练或测试提示(prompt),并将这些提示和相应的答案以元组的形式追加到 ie_prefix
列表中。这些步骤详细地涉及以下操作:
循环遍历字典条目
- 字典遍历:
for key, example in ie_examples.items():
这行代码通过调用.items()
方法在字典ie_examples
上进行迭代,返回每个元素的键 (key
) 和值 (example
)。在这个例子中,key
是病历的标识(如 '病历1','病历2' 等),而example
是与这个键相关联的值,它是一个包含病历描述('sentence')和相应答案('answers')的字典。
构建格式化提示
- 获取病历句子:
sentence = example['sentence']
从当前遍历到的example
字典中取出键为 'sentence' 的值,这个值是描述病历的文本。 - 格式化提示:
formatted_prompt = IE_PATTERN.format(sentence, schema_str_list)
使用之前定义的IE_PATTERN
字符串模板和format
方法,将sentence
和schema_str_list
插入到模板中,生成一个具体的提示。这个提示指导模型从sentence
中抽取属于schema_str_list
中定义的字段。
生成的带prompt的用户输入示例:我叫傅一帆,今年38岁,我从昨天开始感觉头疼,并且伴有腹泻的症状。以前得过肠胃型感冒,对阿莫西林过敏 提取上述句子中“姓名, 年龄, 主诉, 持续时间, 既往病史, 过敏药物”类型的实体,输出JSON格式,如果上述句子没有该信息,则用['未提供']来表示,多个值之间用','分隔。
JSON格式化答案
- 生成JSON格式答案:
answers_json = json.dumps(example['answers'], ensure_ascii=False)
将example
字典中的 'answers' 字典转换为 JSON 格式的字符串。参数ensure_ascii=False
允许字符串包含非ASCII字符,这对于包含中文的数据是必要的。
更新列表
- 追加到列表:
ie_prefix.append((formatted_prompt, answers_json))
将生成的formatted_prompt
和answers_json
组成一个元组,然后追加到列表ie_prefix
中。这样,ie_prefix
列表最终包含了一系列的预设的(提示, 答案)元组,这些元组用于输入大模型的历史中,用来few-shot learning帮助大模型理解任务,给出合适的输出。
代码功能和目的
这段代码用于生成一系列的预设的(提示, 答案)元组,这些元组用于输入大模型的历史中,用来few-shot learning帮助大模型理解任务,给出何时的输出
4.格式化模型输出:
format_output()
:即使使用prompt工程要求大模型输出格式json格式的数据,但大模型返回的输出仍有可能带有额外的信息(在json数据之外有额外的字符串),或是非标准格式的json格式,将非预期格式输出尝试输送给前端或是存入数据库可能导致预料之外的程序行为。format_output()将模型的输出从字符串或其他格式转换为JSON对象。它尝试识别和格式化JSON格式的数据,确保输出是标准的JSON结构。具体方法见另一篇推文:
5.信息抽取推断:
inference()
:这个函数处理实际的文本输入,生成信息抽取的提示,并调用预训练模型进行推断。它使用ie_prefix
列表作为历史记录,来帮助模型理解任务的上下文。
6.模型和分词器的初始化与运行:
- 在
__main__
块中,初始化分词器和模型,然后调用inference()
函数处理一组新的病历文本。这里调用了.cuda()
方法来使用NVIDIA GPU加速计算。