Spaces:
Runtime error
Runtime error
DSXiangLi
commited on
Commit
•
66786b8
1
Parent(s):
1f5a1b1
add self
Browse files- ape/__pycache__/__init__.cpython-38.pyc +0 -0
- ape/__pycache__/ape.cpython-38.pyc +0 -0
- ape/__pycache__/dump.cpython-38.pyc +0 -0
- ape/__pycache__/instance.cpython-38.pyc +0 -0
- ape/__pycache__/llm.cpython-38.pyc +0 -0
- ape/__pycache__/prompt.cpython-38.pyc +0 -0
- ape/data/seed_task.json +4 -0
- ape/dump.py +35 -0
- ape/instance.py +3 -7
- app.py +50 -2
- main.py +34 -0
- self/__pycache__/__init__.cpython-38.pyc +0 -0
- self/__pycache__/generate.cpython-38.pyc +0 -0
- self/__pycache__/prompt.cpython-38.pyc +0 -0
- self/data/__init__.py +1 -0
- self/generate.py +183 -0
- self/prompt.py +37 -0
ape/__pycache__/__init__.cpython-38.pyc
CHANGED
Binary files a/ape/__pycache__/__init__.cpython-38.pyc and b/ape/__pycache__/__init__.cpython-38.pyc differ
|
|
ape/__pycache__/ape.cpython-38.pyc
CHANGED
Binary files a/ape/__pycache__/ape.cpython-38.pyc and b/ape/__pycache__/ape.cpython-38.pyc differ
|
|
ape/__pycache__/dump.cpython-38.pyc
ADDED
Binary file (1.77 kB). View file
|
|
ape/__pycache__/instance.cpython-38.pyc
CHANGED
Binary files a/ape/__pycache__/instance.cpython-38.pyc and b/ape/__pycache__/instance.cpython-38.pyc differ
|
|
ape/__pycache__/llm.cpython-38.pyc
CHANGED
Binary files a/ape/__pycache__/llm.cpython-38.pyc and b/ape/__pycache__/llm.cpython-38.pyc differ
|
|
ape/__pycache__/prompt.cpython-38.pyc
CHANGED
Binary files a/ape/__pycache__/prompt.cpython-38.pyc and b/ape/__pycache__/prompt.cpython-38.pyc differ
|
|
ape/data/seed_task.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"instruction": "将医学手术名称的术语表述标准化。输入是医学手术的名称,输出是对该手术的名称进行修正、标准化,以供医学专业人员更好地理解", "instances": [{"input": "横结肠造口还纳术", "output": "横结肠造口闭合术"}, {"input": "右肾上腺巨大肿瘤切除术", "output": "肾上腺病损切除术"}, {"input": "左侧单侧乳房根治性切除术", "output": "单侧根治性乳房切除术"}, {"input": "经皮三叉神经半月节射频热凝术", "output": "三叉神经半月节射频热凝术"}, {"input": "经内镜支撑喉镜下双侧声带小结摘除术", "output": "内镜下声带病损切除术"}]}
|
2 |
+
{"instruction": "生成医学相关问题的答案。给定一个输入问题,需要根据问题生成相应的输出答案。答案包括临床表现、病因、治疗方法、作用、定义等等,如果有多个问题,返回多问", "instances": [{"input": "治痔疮会影响上班吗,医保有报吗?外痔。有无报销。会否影响工作", "output": "多问"}, {"input": "本人今年16,最近感觉睾丸多长了一块肉,不痛不痒的,是睾丸癌吗", "output": "临床表现(病症表现)"}, {"input": "痔疮手术几个月后,肛门周围有长了一个,吃辣的东西就会肿起来,怎么办?是术后复发了吗?", "output": "多问"}, {"input": "你好,我宝宝8kg,吃美林要吃多少啊?你好,我宝宝8kg,吃美林要吃多少啊?", "output": "用法"}, {"input": "请问月经量多服用什么药?", "output": "适用症"}]}
|
3 |
+
{"instruction": "训练一个问答系统,给定一些医学文本,能够回答用户提问关于该文本内容的问题。每个输入-输出对是一组文本和对应的问题及答案。输出的形式是以下Json格式{\"问题\":$问题, \"回答\":$回答}", "instances": [{"input": "\"胆石症的治疗应区别不同情况分别处理,无症状胆囊结石可不作治疗,但应定期观察并注意良好的饮食习惯。有症状的胆囊结石仍以胆囊切除术为较安全有效的疗法,此外,尚可采用体外震波碎石。胆管结石宜采用以手术为主的综合治疗。胆石症的家庭治疗可采用以下方法:\\n(1)一般治疗 预防和治疗肠道寄生虫病和肠道感染,以降低胆石症的发病率。胆绞痛发作期应禁食脂肪等食物,采用高碳水化合物流质饮食;缓解期应忌食富含胆固醇的食物如脑、肝、肾、蛋黄等。\\n(2)增进胆汁排泄 可选用50%硫酸镁10~15毫升,餐后口服,每日3次;胆盐每次口服0.5~1克,每日3次;去氢胆酸0.25克,每日3次,餐后服用。\\n(3)消除胆绞痛 轻者可卧床休息,右上腹热敷,用硝酸甘油酯0.6毫克,每3~4小时一次,含于舌下;或阿托品0.5毫克,每3~4小时肌肉注射一次。重者应住院治疗。\\n(4)排石疗法以中药治疗为主,若右上腹疼痛有间歇期,无明显发热及黄疸,苔薄白,脉弦,属气滞者,用生大黄6克、木香9克、枳壳9克、金钱草30克、川楝子9克、黄苓9克,水煎服。右上腹痛为持续性,且阵发性加剧,有明显发热及黄疸,舌红苔黄,", "output": "{\"问题\": \"什么类型的胆囊结石可不作治疗?\", \"回答\": \"无症状胆囊结\"}"}, {"input": "反佐配伍的典范,始见于张仲景《伤寒杂病论》,其中记载“干呕、吐涎沫、头痛者吴茱萸汤主之”。患者病机为肝寒犯胃,浊气上逆所致头痛。胃阳不布产生涎沫随浊气上逆而吐出,肝脉与督脉交会于巅顶,肝经寒邪,循经上冲则头痛,以吴茱萸汤主治。可在吴茱萸汤中加入少许黄连反佐,用以防止方中吴茱萸、人参、干姜等品辛热太过,从而达到温降肝胃、泄浊通阳而止头痛的功效。后代医者多在清热剂和温里剂中运用此法。", "output": "{\"问题\": \"《伤寒杂病论》的作者是谁?\", \"回答\": \"张仲景\"}"}, {"input": "疼痛是临床上常见的一种自觉症状。疼痛有虚实之分,实性疼痛多因感受外邪、气滞血瘀、痰浊凝滞,或食积、虫积、结石等阻滞脏腑经脉,气血运行不畅所致,即所谓“不通则痛”;虚性疼痛多因阳气亏虚,精血不足,脏腑经脉失养所致,即所谓“不荣则痛”。", "output": "{\"问题\": \"不荣则痛是什么意思 ?\", \"回答\": \"虚性疼痛多因阳气亏虚,精血不足,脏腑经脉失养所致,即所谓“不荣则痛”。\"}"}, {"input": "黄帝说:什麽叫做所胜?岐伯说:春胜长夏,长夏胜冬,冬胜夏,夏胜秋,秋胜春,这就是时令根据五行规律而互相胜负的情况。同时,时令又依其五行之气的属性来分别影响各脏。黄帝说:怎样知道它们之间的相胜情况呢?岐伯说:首先要推求气候到来的时间,一般从立春开始向下推算。如果时令未到而气候先期来过,称为太过,某气太过就会侵侮其所不胜之��,欺凌其所胜之气,这就叫做气淫;时令以到而气候未到,称为不及,某气不及,则其所胜之气因缺乏制约而妄行,其所生之气因缺乏资助而困弱,其所不胜则更会加以侵迫,这就叫做气迫。所谓求其至,就是要根据时令推求气候到来的早晚,要谨慎地等候时令的变化,气候的到来是可以预期的。如果搞错了时令或违反了时令与气候相合的关系,以致于分不出五行之气当旺的时间,那麽,当邪气内扰,病及于人的时候,好的医生也不能控制了。", "output": "{\"问题\": \"什么叫做太过?\", \"回答\": \"如果时令未到而气候先期来过,称为太过\"}"}, {"input": "\"色素障碍性皮肤病(比如雀斑、黄褐斑)已成为美容的大敌,人们通过各种途径力图祛除色素斑。祛斑类化妆品集祛斑与护肤为一体,既简单方便,又有明显的效果。\\n祛斑化妆品种类繁多,仅我国生产的祛斑化妆品就达百余种,所以,这类制品的成分极其多样并且繁复。传统的祛斑化妆品,其主要祛斑添加物为白降汞、硫黄、倍他米松、2%~3%氢醌、4-异丙基儿茶酚、10%壬二酸等。中药添加剂有黄芪、当归等。还有些美容院使用双氧水、三氯醋酸等药物治疗色素增生。新近问世的生物祛斑化妆品,其添加物是一些生物制品,比如曲酸及其衍生物,果酸,以及熊果昔、胎盘和海藻提取物等。\\n实践证明,传统的祛斑化妆品中,疗效较肯定的添加药有2%~3%氢醌,10%壬二酸等。但是氢醌有一定刺激性,要谨,慎使用,也不能随意增大浓度,并且长期使用传统祛斑化妆品,不但黑色素消退不理想,而且会引起皮肤刺激,出现皮肤灼热、潮红、脱屑等症状,甚至会出现色斑复现、加深或白斑化等不良反应。使用双氧水、三氯醋酸等药物若不慎,也会出现毁容性不良反应,所以必须慎用。然而,新型的生物祛斑化妆品在治疗色素沉着症中独具特色。比如曲酸祛斑霜,其主要成分为曲酸。曲酸的作用", "output": "{\"问题\": \"祛斑类化妆品有什么功能?\", \"回答\": \"祛斑与护肤\"}"}]}
|
4 |
+
{"instruction": "给定药品信息和用途说明,根据用途说明提取出药品的主治功能", "instances": [{"input": "6g*12袋国家医保目录(乙类)1.收缩子宫:新生化颗粒使DNA含量和子宫利用葡萄糖能力增加,促进子宫蛋白质合成及子宫增生,以促进子宫收缩,从而起到止血并排出瘀血的目的。实验室研究表明,新生化颗粒能明显增加大鼠离体子宫的收缩张力、收缩频率和收缩振幅,且呈剂量依赖性关系。冲洗药液后,子宫活动仍可恢复到正常状态。2.镇痛:实验室研究表明,新生化颗粒能明显减少大鼠扭体次数。3.抗血小板凝聚及抗", "output": "[\"收缩子宫\", \"止血\", \"排出瘀血\", \"抗血小板凝聚\"]"}, {"input": "10g*6袋/盒铝塑复合膜袋装,10g*6袋/盒。尚不明确。本品为颗粒剂。江苏康缘药业股份有限公司温水冲服,一次1袋,一日2次。孕妇及月经过多者忌服。活血行气,化瘀止痛。用于气滞血瘀所致的痛经。症见月经期小腹胀痛拒拨,经血不畅,血色紫黯成块,乳房胀痛,腰部酸痛尚不明确。如与其它药物同时使用可能会发生药物相互作用,详情请咨询药师或医师。", "output": "[\"活血行气\"]"}, {"input": "具有促进造血和止血作用。本品能促进环磷酰胺所致白细胞总数下降的恢复,提高失血小鼠的血红蛋白含量,缩短小鼠出血时间和血浆复钙时间。具有雌激素样作用。本品可增加雌鼠子宫重量,增高大鼠子宫指数和雌二醇含量,动物出现动情期的比率增多。具有保肝作用。本品可拮抗D-氨基半乳糖所致的急性肝损伤大鼠谷丙转氨酶和谷草转氨酶值的升高;增加四氯化碳所致慢性肝损害大鼠的总蛋白和白蛋白含量。具有增强免疫作用。本品能", "output": "[\"造血\", \"止血\", \"保肝\", \"增强免疫\"]"}, {"input": "与其他药物同时使用可能会发生药物相互作用,详情请咨询医师或药师。孕妇禁用。口服,一次4粒,一日3次。妇科苗药,用于湿热下注型盆腔炎、阴道炎、慢性宫颈炎、痛经贵州远程制药有限责任公司贵州远程制药有限责任公司苗医:巢窝讲港,布发讲港。嘎溜纳洛,修洼凯钠。中医:活血化瘀,清热燥湿。用于湿热下注型盆腔炎、阴道炎、慢性宫颈炎,症见赤白带下、阴痒、出血、痛经杠板归、黄柏、连翘、益母草", "output": "[\"活血化瘀\", \"清热燥湿\"]"}, {"input": "南京同仁堂药业有限责任公司尚不明确。6袋/盒。舒肝养血,理气解郁。用于肝气郁结所致乳癖,症见经前乳房胀痛、两胁胀痛、乳房结节、经前疼痛加重;乳腺增生本品为���棕色至棕褐色颗粒;味微甜、微苦。国家医保目录(乙类),中药保护品种二级尚不明确。开水冲服。一次1袋,一日3次;20天为一疗程,或尊医嘱。5g*6袋孕妇慎服。", "output": "[\"理气解郁\"]"}]}
|
ape/dump.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*-coding:utf-8 -*-
|
2 |
+
"""
|
3 |
+
Dump Instruction Data for SELF
|
4 |
+
"""
|
5 |
+
import json
|
6 |
+
from ape.instance import LoadFactory
|
7 |
+
|
8 |
+
|
9 |
+
def make_instruct_data(instruction, input_output_list):
|
10 |
+
data = {
|
11 |
+
'instruction': instruction,
|
12 |
+
'instances': []
|
13 |
+
}
|
14 |
+
for i, j in input_output_list:
|
15 |
+
data['instances'].append({'input': i, 'output': j})
|
16 |
+
return data
|
17 |
+
|
18 |
+
|
19 |
+
def seed_file(file_name='./ape/data/seed_task.json', n_instances=5):
|
20 |
+
instruction = {
|
21 |
+
'paraphase': '将医学手术名称的术语表述标准化。输入是医学手术的名称,输出是对该手术的名称进行修正、标准化,以供医学专业人员更好地理解',
|
22 |
+
'search_intent': '生成医学相关问题的答案。给定一个输入问题,需要根据问题生成相应的输出答案。答案包括临床表现、病因、治疗方法、作用、定义等等,如果有多个问题,返回多问',
|
23 |
+
'qa_generation': '训练一个问答系统,给定一些医学文本,能够回答用户提问关于该文本内容的问题。每个输入-输出对是一组文本和对应的问题及答案。输出的形式是以下Json格式{"问题":$问题, "回答":$回答}',
|
24 |
+
'entity': '给定药品信息和用途说明,根据用途说明提取出药品的主治功能'
|
25 |
+
}
|
26 |
+
|
27 |
+
with open(file_name, 'w', encoding='UTF8') as f:
|
28 |
+
for task, instruct in instruction.items():
|
29 |
+
sample = LoadFactory[task]()[:n_instances]
|
30 |
+
data = make_instruct_data(instruct, sample)
|
31 |
+
f.write(json.dumps(data, ensure_ascii=False) + '\n')
|
32 |
+
|
33 |
+
|
34 |
+
if __name__ == '__main__':
|
35 |
+
seed_file()
|
ape/instance.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*-coding:utf-8 -*-
|
|
|
2 |
import json
|
3 |
import random
|
4 |
import pandas as pd
|
@@ -103,24 +104,19 @@ def load_entity(file='./ape/data/entity_train.json'):
|
|
103 |
data = []
|
104 |
raw_data = json.load(open(file, encoding='UTF8'))
|
105 |
for i in raw_data:
|
106 |
-
input = i['text']
|
107 |
output = []
|
108 |
for j in i['labels']:
|
109 |
##拆分成单实体任务类型
|
110 |
-
if j[1] =='DRUG_EFFICACY':
|
111 |
output.append(j[-1])
|
112 |
output = json.dumps(output, ensure_ascii=False)
|
113 |
data.append((input, output))
|
114 |
return data
|
115 |
|
116 |
|
117 |
-
with open('./ape/data/entity_train.json', 'r', encoding='UTF8') as f:
|
118 |
-
for i in f.readlines():
|
119 |
-
print(i)
|
120 |
-
|
121 |
LoadFactory = {
|
122 |
'paraphase': load_paraphase,
|
123 |
-
# 'event_extract': load_event_extraction,
|
124 |
'search_intent': load_intent,
|
125 |
'qa_generation': load_qa,
|
126 |
'entity': load_entity
|
|
|
1 |
# -*-coding:utf-8 -*-
|
2 |
+
import re
|
3 |
import json
|
4 |
import random
|
5 |
import pandas as pd
|
|
|
104 |
data = []
|
105 |
raw_data = json.load(open(file, encoding='UTF8'))
|
106 |
for i in raw_data:
|
107 |
+
input = re.sub(r'\s{1,}', '',i['text'][:200]) # 对文本进行截断,不然太长了。。。
|
108 |
output = []
|
109 |
for j in i['labels']:
|
110 |
##拆分成单实体任务类型
|
111 |
+
if j[1] =='DRUG_EFFICACY' and j[-1] in input:
|
112 |
output.append(j[-1])
|
113 |
output = json.dumps(output, ensure_ascii=False)
|
114 |
data.append((input, output))
|
115 |
return data
|
116 |
|
117 |
|
|
|
|
|
|
|
|
|
118 |
LoadFactory = {
|
119 |
'paraphase': load_paraphase,
|
|
|
120 |
'search_intent': load_intent,
|
121 |
'qa_generation': load_qa,
|
122 |
'entity': load_entity
|
app.py
CHANGED
@@ -4,7 +4,8 @@ import gradio as gr
|
|
4 |
from ape.instance import LoadFactory
|
5 |
from ape.prompt import MyTemplate
|
6 |
from ape.ape import *
|
7 |
-
|
|
|
8 |
|
9 |
with gr.Blocks(title="Automatic Prompt Engineer", theme=gr.themes.Glass()) as demo:
|
10 |
gr.Markdown("# Automatic Prompt Engineer")
|
@@ -86,9 +87,45 @@ with gr.Blocks(title="Automatic Prompt Engineer", theme=gr.themes.Glass()) as de
|
|
86 |
with gr.Column(scale=1):
|
87 |
test_score = gr.Textbox(lines=1, value="", label="Log(p)", disabled=True)
|
88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
"""
|
91 |
-
Callback
|
92 |
"""
|
93 |
# 1. 选择已有任务/上传文件,实例化Instance
|
94 |
load_button.click(load_task, [task, file], [instance, load_flag])
|
@@ -111,4 +148,15 @@ with gr.Blocks(title="Automatic Prompt Engineer", theme=gr.themes.Glass()) as de
|
|
111 |
# 7. 输入指令打分
|
112 |
score_button.click(score_single, [eval_prompt, instance, score_instruction, openai_key], [test_score])
|
113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
demo.launch(show_error=True)
|
|
|
4 |
from ape.instance import LoadFactory
|
5 |
from ape.prompt import MyTemplate
|
6 |
from ape.ape import *
|
7 |
+
from self.generate import init_instance
|
8 |
+
from self.prompt import self_prompt
|
9 |
|
10 |
with gr.Blocks(title="Automatic Prompt Engineer", theme=gr.themes.Glass()) as demo:
|
11 |
gr.Markdown("# Automatic Prompt Engineer")
|
|
|
87 |
with gr.Column(scale=1):
|
88 |
test_score = gr.Textbox(lines=1, value="", label="Log(p)", disabled=True)
|
89 |
|
90 |
+
gr.Markdown('\n\n')
|
91 |
+
gr.Markdown('--------')
|
92 |
+
gr.Markdown('\n\n')
|
93 |
+
gr.Markdown("# SELF INSTRUCT")
|
94 |
+
gr.Markdown('## 第一步:输入参数并上传数据')
|
95 |
+
with gr.Row():
|
96 |
+
with gr.Column():
|
97 |
+
openai_key2 = gr.Textbox(type='password', label='输入 API key')
|
98 |
+
with gr.Row():
|
99 |
+
n_human = gr.Slider(label="人工指令数", minimum=1, maximum=5, step=2, value=1)
|
100 |
+
n_machine = gr.Slider(label="机器指令数", minimum=1, maximum=5, step=1, value=1)
|
101 |
+
n_instruct = gr.Slider(label="生成指令数", minimum=1, maximum=5, step=5, value=1, help="生成指令数>人工+机器")
|
102 |
+
seed_file = gr.File(label='上传json文件, 格式参考./self/data/seed_task.json')
|
103 |
+
|
104 |
+
with gr.Column():
|
105 |
+
self_prompt_input = gr.Textbox(max_lines=100, lines=3, interative=True,
|
106 |
+
placeholder=self_prompt,
|
107 |
+
value='', label="Prompt for self-instruct")
|
108 |
+
init_self = gr.Button("初始化")
|
109 |
+
self_instance = gr.State()
|
110 |
+
|
111 |
+
gr.Markdown('\n\n')
|
112 |
+
gr.Markdown('## 第二步:采样few-shot并生成,每点一次会重采样并生成,生成结果会累计')
|
113 |
+
with gr.Row():
|
114 |
+
with gr.Column(scale=1):
|
115 |
+
gr.Markdown("### 本轮采样few-shot指令")
|
116 |
+
fewshot = gr.Textbox(label='采样few-shot')
|
117 |
+
with gr.Column(scale=1):
|
118 |
+
gr.Markdown("### 生成新的指令和样本")
|
119 |
+
gen_data = gr.JSON(label='新生成指令样本')
|
120 |
+
|
121 |
+
with gr.Row():
|
122 |
+
with gr.Column(scale=7):
|
123 |
+
generate_instruct_button = gr.Button("指令生成")
|
124 |
+
with gr.Column(scale=1):
|
125 |
+
counter = gr.Textbox()
|
126 |
|
127 |
"""
|
128 |
+
APE Callback
|
129 |
"""
|
130 |
# 1. 选择已有任务/上传文件,实例化Instance
|
131 |
load_button.click(load_task, [task, file], [instance, load_flag])
|
|
|
148 |
# 7. 输入指令打分
|
149 |
score_button.click(score_single, [eval_prompt, instance, score_instruction, openai_key], [test_score])
|
150 |
|
151 |
+
|
152 |
+
"""
|
153 |
+
SELF Callback
|
154 |
+
"""
|
155 |
+
# 1. 加载种子文件
|
156 |
+
init_self.click(init_instance, inputs=[seed_file, openai_key2, n_human, n_machine, n_instruct,self_prompt_input],
|
157 |
+
outputs=[self_instance])
|
158 |
+
|
159 |
+
# 2. 生成
|
160 |
+
generate_instruct_button.click(generate, inputs=[self_instance], outputs=[fewshot, gen_data, counter])
|
161 |
+
|
162 |
demo.launch(show_error=True)
|
main.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*-coding:utf-8 -*-
|
2 |
+
"""
|
3 |
+
Run SELF
|
4 |
+
"""
|
5 |
+
|
6 |
+
import numpy as np
|
7 |
+
from self.generate import SELF
|
8 |
+
|
9 |
+
threshold = 0.7 # 当最近几个指令相似度都很高的时候停止
|
10 |
+
|
11 |
+
|
12 |
+
def main(seed_file, output_file, openai_key, n_human, n_machine, n_instruct, max_iter, max_gen):
|
13 |
+
instance = SELF(seed_file, openai_key, n_human, n_machine, n_instruct, None)
|
14 |
+
|
15 |
+
n_iter = 0
|
16 |
+
while n_iter < max_iter and instance.n_keep < max_gen:
|
17 |
+
instance.step()
|
18 |
+
n_iter +=1
|
19 |
+
print(f'已生成{instance.n_gen} 可用{instance.n_keep}')
|
20 |
+
if n_iter >3 and np.average([i['avg_similarity_score'] for i in instance.machine_instruction_data[-5:]] )> threshold:
|
21 |
+
break
|
22 |
+
|
23 |
+
# dump file
|
24 |
+
instance.dump_file(output_file)
|
25 |
+
|
26 |
+
|
27 |
+
|
28 |
+
if __name__ == '__main__':
|
29 |
+
seed_file = './ape/data/seed_task.json'
|
30 |
+
openai_key ='a'
|
31 |
+
n_human=2
|
32 |
+
n_machine=1
|
33 |
+
n_instruct=5
|
34 |
+
instance = SELF(seed_file, openai_key, n_human, n_machine, n_instruct, None)
|
self/__pycache__/__init__.cpython-38.pyc
ADDED
Binary file (146 Bytes). View file
|
|
self/__pycache__/generate.cpython-38.pyc
ADDED
Binary file (6.64 kB). View file
|
|
self/__pycache__/prompt.cpython-38.pyc
ADDED
Binary file (2.29 kB). View file
|
|
self/data/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# -*-coding:utf-8 -*-
|
self/generate.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*-coding:utf-8 -*-
|
2 |
+
import re
|
3 |
+
import numpy as np
|
4 |
+
import os
|
5 |
+
import json
|
6 |
+
import random
|
7 |
+
from self.prompt import self_prompt, gen_few_shot_prompt
|
8 |
+
from rouge_score import rouge_scorer
|
9 |
+
|
10 |
+
from langchain.prompts import PromptTemplate
|
11 |
+
from functools import partial
|
12 |
+
from langchain.chains.llm import LLMChain
|
13 |
+
from langchain.llms import OpenAI
|
14 |
+
from multiprocessing import Pool
|
15 |
+
|
16 |
+
|
17 |
+
def is_all_chinese(strs):
|
18 |
+
for _char in strs:
|
19 |
+
if not '\u4e00' <= _char <= '\u9fa5':
|
20 |
+
return False
|
21 |
+
return True
|
22 |
+
|
23 |
+
|
24 |
+
class ChineseTokenizer():
|
25 |
+
def tokenize(self, text):
|
26 |
+
tokens = [i.strip() for i in text if i.strip()]
|
27 |
+
return tokens
|
28 |
+
|
29 |
+
|
30 |
+
class SELF(object):
|
31 |
+
n_instance = 3 # 指令不够样本来凑,每个指令加入多个instance
|
32 |
+
prefix = "{id}. 指令:"
|
33 |
+
blacklist = ['图片', '图像', '文件', '作图', '绘画', '视频', '音频', '音乐', '流程图']
|
34 |
+
|
35 |
+
def __init__(self, seed_file, openai_key, n_human, n_machine, n_instruct, prompt):
|
36 |
+
self.llm = OpenAI(openai_api_key=openai_key, temperature=1,
|
37 |
+
stop=[f'\n{n_instruct}', '{n_instruct}', '{n_instruct}.'], # 当已生成足够的指令则停止
|
38 |
+
logit_bias={'50259': -100} # 不生成最后的停止符#
|
39 |
+
) # 默认davinci-003
|
40 |
+
self.n_human, self.n_machine, self.n_instruct = n_human, n_machine, n_instruct
|
41 |
+
self.n_gen, self.n_keep = 0, 0
|
42 |
+
self.human_instruction_data = []
|
43 |
+
self.machine_instruction_data = []
|
44 |
+
self.scorer = None # rougeL用于文本相似度计算
|
45 |
+
self.all_instruction_tokens = [] # 全部指令,用于新指令消重
|
46 |
+
self.all_instruction = [] # 全部指令,用于新指令消重
|
47 |
+
self.sample_few_shot = None
|
48 |
+
self.load_seed_task(seed_file)
|
49 |
+
self.init(prompt)
|
50 |
+
|
51 |
+
def load_seed_task(self, seed_file):
|
52 |
+
instruction_data = []
|
53 |
+
with open(seed_file, 'r', encoding='UTF8') as f:
|
54 |
+
for i in f.readlines():
|
55 |
+
tmp = json.loads(i)
|
56 |
+
for j in range(min(len(tmp['instances']), SELF.n_instance)):
|
57 |
+
instruction_data.append({'instruction': tmp['instruction'],
|
58 |
+
'input': tmp['instances'][j]['input'],
|
59 |
+
'output': tmp['instances'][j]['output']})
|
60 |
+
self.human_instruction_data = instruction_data
|
61 |
+
|
62 |
+
def init(self, prompt):
|
63 |
+
if not prompt:
|
64 |
+
prompt = self_prompt
|
65 |
+
self.chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(prompt))
|
66 |
+
self.scorer = rouge_scorer.RougeScorer(['rougeL'], use_stemmer=False, tokenizer=ChineseTokenizer())
|
67 |
+
self.all_instruction = self.human_instruction_data + self.machine_instruction_data
|
68 |
+
self.all_instruction_tokens = [self.scorer._tokenizer.tokenize(i['instruction']) for i in
|
69 |
+
self.all_instruction]
|
70 |
+
|
71 |
+
@property
|
72 |
+
def first_id(self):
|
73 |
+
# 第一个机器生成的指令id,前面是few-shot样例
|
74 |
+
return int(self.n_human + min(self.n_machine, len(self.machine_instruction_data)) + 1)
|
75 |
+
|
76 |
+
def generate(self):
|
77 |
+
"""
|
78 |
+
新指令生成
|
79 |
+
1. 采样few-shot[n_human + n_machine]
|
80 |
+
2. 生成指令
|
81 |
+
3. 解析模型结果得到新的指令样本
|
82 |
+
"""
|
83 |
+
# sample
|
84 |
+
seed_sample = random.sample(self.human_instruction_data, self.n_human)
|
85 |
+
machine_sample = random.sample(self.machine_instruction_data,
|
86 |
+
min(self.n_machine, len(self.machine_instruction_data)))
|
87 |
+
self.sample_few_shot = seed_sample + machine_sample
|
88 |
+
# build few-shot
|
89 |
+
few_shot = gen_few_shot_prompt(self.sample_few_shot)
|
90 |
+
few_shot += SELF.prefix.format(id=self.first_id) # 新生成的指令
|
91 |
+
print(few_shot)
|
92 |
+
# generate
|
93 |
+
result = self.chain({'few_shot': few_shot})
|
94 |
+
print(result)
|
95 |
+
return result
|
96 |
+
|
97 |
+
def decode_response(self, response):
|
98 |
+
if response is None:
|
99 |
+
return []
|
100 |
+
raw_instruct = SELF.prefix.format(id=self.first_id) + response['text']
|
101 |
+
raw_instruct = raw_instruct.split('###')
|
102 |
+
instruction_data = []
|
103 |
+
|
104 |
+
for id, inst in enumerate(raw_instruct):
|
105 |
+
# 因为超长停止的最后一个指令往往被阶段,这里直接丢弃
|
106 |
+
if id == len(raw_instruct) and response['finish_reason'] == 'length':
|
107 |
+
continue
|
108 |
+
|
109 |
+
splitted_data = re.split(f"{id + self.first_id}\.\s+(指令|输入|输出):", inst)
|
110 |
+
if len(splitted_data) != 7:
|
111 |
+
continue # 生成部分缺失或格式错误
|
112 |
+
else:
|
113 |
+
inst = splitted_data[2].strip()
|
114 |
+
input = splitted_data[4].strip()
|
115 |
+
input = "" if input.lower() == '<无输入>' else input
|
116 |
+
output = splitted_data[6].strip()
|
117 |
+
|
118 |
+
print({'instruction': inst, 'input': input, 'output': output})
|
119 |
+
# 过滤过长,过断的指令
|
120 |
+
if len(inst) <= 3 or len(inst) >= 100:
|
121 |
+
continue
|
122 |
+
|
123 |
+
# 过滤有疑似模型无法执行的指令
|
124 |
+
if any((i in inst for i in SELF.blacklist)):
|
125 |
+
continue
|
126 |
+
|
127 |
+
# 如果指令开头并非中文
|
128 |
+
if not is_all_chinese(inst[:3]):
|
129 |
+
continue
|
130 |
+
|
131 |
+
instruction_data.append({'instruction': inst, 'input': input, 'output': output})
|
132 |
+
return instruction_data
|
133 |
+
|
134 |
+
def sim_filter(self, instruction_data):
|
135 |
+
## 过滤和已有指令池相似度过高的指令,保证多样性, 使用Rouge-L最长公共子串
|
136 |
+
keep_instruction = []
|
137 |
+
for inst in instruction_data:
|
138 |
+
inst_tokens = self.scorer._tokenizer.tokenize(inst['instruction'])
|
139 |
+
with Pool(os.cpu_count()) as p:
|
140 |
+
rouge_scores = p.map(partial(rouge_scorer._score_lcs, self.all_instruction_tokens), inst_tokens)
|
141 |
+
rouge_l = [score.fmeasure for score in rouge_scores]
|
142 |
+
top10_sim_inst = {
|
143 |
+
self.all_instruction[i]: rouge_l[i] for i in np.argsort(rouge_l)[-10:][::-1]
|
144 |
+
}
|
145 |
+
print(top10_sim_inst)
|
146 |
+
if max(rouge_l) > 0.7:
|
147 |
+
continue
|
148 |
+
inst['most_similar_instructions'] = top10_sim_inst
|
149 |
+
inst['avg_similarity_score'] = float(np.mean(rouge_l))
|
150 |
+
self.all_instruction.append(inst['instruction'])
|
151 |
+
self.all_instruction_tokens.append(inst_tokens)
|
152 |
+
keep_instruction.append(inst)
|
153 |
+
return keep_instruction
|
154 |
+
|
155 |
+
def step(self):
|
156 |
+
new_instruct_data = self.generate()
|
157 |
+
keep_instruct_data = self.sim_filter(new_instruct_data)
|
158 |
+
self.n_gen += len(new_instruct_data)
|
159 |
+
self.n_keep += len(keep_instruct_data)
|
160 |
+
self.machine_instruction_data += keep_instruct_data
|
161 |
+
return keep_instruct_data # for gradio output only
|
162 |
+
|
163 |
+
def dump_file(self, output_file):
|
164 |
+
with open(output_file, 'w', encoding='UTF8') as f:
|
165 |
+
for i in self.machine_instruction_data:
|
166 |
+
f.write(json.dumps(i, ensure_ascii=False) + '\n')
|
167 |
+
|
168 |
+
|
169 |
+
# Only Used for gradio display
|
170 |
+
def init_instance(seed_file, openai_key, n_human, n_machine, n_instruct, prompt):
|
171 |
+
# 允许用户输入prompt修改前缀指令命令
|
172 |
+
if not prompt:
|
173 |
+
prompt = self_prompt
|
174 |
+
self_instance = SELF(seed_file.name, openai_key, n_human, n_machine, n_instruct, prompt)
|
175 |
+
return self_instance
|
176 |
+
|
177 |
+
|
178 |
+
def generate(self_instance):
|
179 |
+
keep_instruct_data = self_instance.step()
|
180 |
+
|
181 |
+
return (json.dumps(self_instance.sample_few_shot, ensure_ascii=False),
|
182 |
+
json.dumps(keep_instruct_data, ensure_ascii=False),
|
183 |
+
f'已生成{self_instance.n_gen} 可用{self_instance.n_keep}')
|
self/prompt.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*-coding:utf-8 -*-
|
2 |
+
import re
|
3 |
+
|
4 |
+
#20个简化成5个
|
5 |
+
self_prompt = """你需要想出{n_instruct}个不同的任务指令。这些任务指令将输入GPT模型,我们将评估GPT模型完成指令的情况。
|
6 |
+
以下是要求:
|
7 |
+
1. 尽量不要在每个指令中重复使用动词,以最大化多样性
|
8 |
+
2. 指令的表达形式需要多样化。例如你可以把问题和祈使句结合起来
|
9 |
+
3. 指令的类型应该多样化,包括但不限于开放式生成、分类、抽取、问答、文本编辑等等
|
10 |
+
4. 指令应该是GPT模型可以完成的任务。例如,指令不能是输出图像或者视频,另一个例子,不要让助手在下午5点叫醒你或设置提醒,因为GPT不能执行任何动作
|
11 |
+
5. 指令必须是中文
|
12 |
+
6. 指令应该是1到2句话,可以是祈使句或问句。
|
13 |
+
7. 你应该为指令生成一个合适的输入。输入字段应该包含为指令提供的一个具体示例。它应该涉及真实的数据,而不应该包含简单的占位符。输入应该提供足够的内容,使指令具有挑战性,但理想情况下不应超过100个单词。
|
14 |
+
8. 不是所有的指令都需要输入。例如,当一个指令询问一些一般信息时,“世界上最高的山峰是什么”,就不需要提供具体的上下文。在这种情况下,我们只需在输入字段中放置“<无输入>”。
|
15 |
+
9. 输出应该是对指令和输入的合适回应。确保输出少于100个单词。
|
16 |
+
{n_instruct}个任务的列表:
|
17 |
+
{few_shot}
|
18 |
+
"""
|
19 |
+
|
20 |
+
one_shot_prompt = "###\n{id}. 指令:{instruction}\n{id}. 输入:{input}\n{id}. 输出:{output}\n"
|
21 |
+
|
22 |
+
|
23 |
+
def gen_one_shot_prompt(id, instruction, input, output):
|
24 |
+
instruction = re.sub(r'\s+'," ",instruction).strip().rstrip(":")
|
25 |
+
input = '<无输入>' if input == '' else input
|
26 |
+
few_shot = one_shot_prompt.format(id=id, instruction=instruction, input=input, output=output)
|
27 |
+
return few_shot
|
28 |
+
|
29 |
+
|
30 |
+
def gen_few_shot_prompt(instruction_data):
|
31 |
+
surfix = '###\n'
|
32 |
+
prompt = ''
|
33 |
+
for i, data in enumerate(instruction_data):
|
34 |
+
prompt += gen_one_shot_prompt(i+1, data['instruction'], data['input'], data['output'])
|
35 |
+
prompt +=surfix
|
36 |
+
return prompt
|
37 |
+
|