diff --git a/LLM.py b/LLM.py index 15b2ecd..d6a81c0 100644 --- a/LLM.py +++ b/LLM.py @@ -15,9 +15,7 @@ def __init__(self, model_path :str): super().__init__() print("正在从本地加载模型...") self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) - print("完成AutoTokenizer...") self.model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(torch.bfloat16).cuda() - print("完成AutoModelForCausalLM...") self.model = self.model.eval() print("完成本地模型的加载") diff --git a/README.md b/README.md new file mode 100755 index 0000000..35be919 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +
+ +
+ +# SpringFestQA(年关走亲访友渡劫助手) + +## 介绍 + SpringFestQA(年关走亲访友渡劫助手)收集了网络上中国春节的怼亲戚语录, + 基于InternLM2进行微调以及检索增强链生成的模仿年轻人语气对亲戚提问作出巧妙回答的语言模型。 + 过年走亲访友过程中,难免遇到亲戚的热辣提问让你不知所措,还在为躺在床上才回想起来如何回怼而感到懊恼吗? + 直接将棘手的提问交给大模型,让亲戚哑口无言。 + + 在可视化网页界面中,我们提供了三种不同的回答风格:委婉认真、转换话题和阴阳怪气, + 以满足不同性格的人的回答需求。通过直接点击右侧的按钮,可以生成对应风格的回答。 + +## OpenXLab体验地址: +``` +https://openxlab.org.cn/apps/detail/SijieLyu/SpringFestQA +``` +## SpringFestQA整体流程框架 +待流程图完成后补充到这里 + +## 数据收集 + 数据集放在本仓库的data目录下: +### 1)MBTI + 为开源的MBTI中文版本数据集,jason格式,包含四个主题:感情、收入、学业、房子这四类。 + 具体可参考 https://github.com/PKU-YuanGroup/Machine-Mindset/tree/main/datasets/behaviour +### 2)origin_data + 用ChatGLM生成的五种风格的数据库,csv格式,分别是诙谐幽默、转换话题、委婉回答、阴阳怪气、故作神秘/深沉, + 每种数据1万条,保证数据量足够,并在调试过程中优化为3种稳定且差异性输出的风格。 +### 3)alpaca_data + 以origin_data为原始数据转换成的json格式数据,转换代码可参考convert.py + +## 基于大语言模型的数据增广方法 + - 先行人工拟定对于回答的基于不同风格的少量样例数据 + - 根据少量数据构造对应的prompt + - 将prompt输入LLM生成更多的数据语料 + - 对语料进行人工审核构建对应的训练数据集 + +## 模型微调 + 依据MBTI数据和QA对,使用Xtuner对InternLM-Chat-7B的性格和内在知识储备进行部分参数微调,形成微调后的模型SpringFest。 + 性格的训练出来但回答不太有用,要多轮对话才能体现人格,但大模型多轮对话能力有限,发现效果不如预期。【可以在gradio页面上增加I/E选项,后端可调用不同模型】 + +## 构建知识库(RAG) + 依据QA对,基于langchain框架构建进行embedding,形成外挂知识库,可以针对用户的 query 进行语义向量检索,得到与用户提问相关的知识片段。【可以在页面上增加prompt提示】 + Prompt调优 + 当知识库内容不足时,转而激发微调后的SpringFest大模型能力,用incontext-learning的方法给模型展示正确的例子生成回答, + 包括但不限于: + 1.使用system prompt让模型明确任务; + 2.通过委婉认真、转移话题、阴阳怪气三个风格的内置prompt,满足用户自行切换回答风格的需求。 + [图片] + +## ModelScope模型 +### 模型权重下载(代码中已内置下载,不需要操作) + https://www.modelscope.cn/binbeing/SpringFestQA.git + SpringFestQA是InternLM2为底座模型,使用春节话题数据集和性格数据集,通过XTuner进行微调后获得的模型。可安装modelscope库后按以下命令进行下载: + ``` + import torch + from modelscope import snapshot_download, AutoModel, AutoTokenizer + import os + model_dir = snapshot_download('binbeing/SpringFestQA', cache_dir='./') + ``` +## 模型部署 +- 部署到应用平台(以OpenXLab为例) +仅需要 Fork 本仓库,然后在 OpenXLab 上创建一个新的项目,将 Fork 的仓库与新建的项目关联,即可在 OpenXLab 上部署 SpringFestQA。 +- 部署到本地(以InternStudio开发机为例) + ``` + git clone https://github.com/Lyusijie/SpringFestQA.git + + python app.py + + ssh -CNg -L 7860:127.0.0.1:7860 root@ssh.intern-ai.org.cn -p 33471做端口转发( + 其中33471改为自己开发机端口) + ``` +- 本地网页打开:127.0.0.1:7860 diff --git a/app.py b/app.py index 5371c62..53efb8a 100644 --- a/app.py +++ b/app.py @@ -1,166 +1,2 @@ -__import__('pysqlite3') -import sys -sys.modules['sqlite3'] = sys.modules.pop('pysqlite3') - -# 导入必要的库 -import gradio as gr -from langchain.vectorstores import Chroma -from langchain.embeddings.huggingface import HuggingFaceEmbeddings -from LLM import InternLM_LLM -from langchain.prompts import PromptTemplate -import torch -from modelscope import snapshot_download, AutoModel, AutoTokenizer import os -from langchain.chains import RetrievalQA -from langchain import PromptTemplate, LLMChain -from langchain.chains import ConversationalRetrievalChain -from langchain.chains.question_answering import load_qa_chain -from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT - -prompt_template_qa="""直接回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 - - 问题: {question} - 内容:{context} - 回答:""" -def change_prompt1(*args): - global prompt_template_qa - prompt_template_qa = """委婉认真地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 - 问题: {question} - 内容:{context} - 回答:""" - -def change_prompt2(*args): - global prompt_template_qa - prompt_template_qa = """转移话题地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 - 问题: {question} - 内容:{context} - 回答:""" - -def change_prompt3(*args): - global prompt_template_qa - prompt_template_qa = """阴阳怪气地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 - 问题: {question} - 内容:{context} - 回答:""" - -def init(): - model_dir = snapshot_download('Shanghai_AI_Laboratory/internlm-chat-7b' - , cache_dir='./') - os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com' - # 下载模型 - os.system('huggingface-cli download --resume-download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 --local-dir sentence-transformer') - - -def load_chain(): - # 加载问答链 - # 定义 Embeddings - embeddings = HuggingFaceEmbeddings(model_name="sentence-transformer") - - # 向量数据库持久化路径 - persist_directory = 'data_base/vector_db/chroma' - - # 加载数据库 - vectordb = Chroma( - persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上 - embedding_function=embeddings - ) - - llm = InternLM_LLM(model_path = "Shanghai_AI_Laboratory/internlm-chat-7b") - - # 定义一个 Prompt Template - template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language. - Chat History: {chat_history} - Follow Up Input: {question} - Standalone question: """ - prompt_qg = PromptTemplate( - template=template, - input_variables=["chat_history", "question"], - ) - global prompt_template_qa - - prompt_qa = PromptTemplate( - template=prompt_template_qa, - input_variables=["context", "question"] - ) - question_generator = LLMChain(llm=llm, prompt=prompt_qg) - doc_chain = load_qa_chain(llm=llm, chain_type="stuff", prompt=prompt_qa) - - # 运行 chain - qa_chain = ConversationalRetrievalChain(retriever=vectordb.as_retriever(),question_generator=question_generator,combine_docs_chain=doc_chain,) - return qa_chain - -class Model_center(): - """ - 存储问答 Chain 的对象 - """ - init() - def __init__(self): - self.chain = load_chain() - - def qa_chain_self_answer(self, question: str, chat_history:list): - """ - 调用问答链进行回答 - """ - chat_history_tuples = [] - #for message in chat_history: - #chat_history_tuples.append((message[0], message[1])) - chat_history_tuples = tuple(tuple(x) for x in chat_history) - if question == None or len(question) < 1: - return "", chat_history - try: - chat_history.append( - (question, self.chain({"question": question, "chat_history": chat_history_tuples})["answer"])) - # 将问答结果直接附加到问答历史中,Gradio 会将其展示出来 - return "", chat_history - except Exception as e: - return e, chat_history - - -model_center = Model_center() - -block = gr.Blocks() -with block as demo: - with gr.Row(equal_height=True): - with gr.Column(scale=15): - gr.Markdown("""

SpringFestQA

-
年关走亲访友渡劫助手
- """) - # gr.Image(value=LOGO_PATH, scale=1, min_width=10,show_label=False, show_download_button=False) - - with gr.Row(): - with gr.Column(scale=4): - chatbot = gr.Chatbot(height=450, show_copy_button=True) - # 创建一个文本框组件,用于输入 prompt。 - msg = gr.Textbox(label="Prompt/问题") - - with gr.Row(): - # 创建提交按钮。 - db_wo_his_btn = gr.Button("Chat") - with gr.Row(): - # 创建一个清除按钮,用于清除聊天机器人组件的内容。 - clear = gr.ClearButton( - components=[chatbot], value="Clear console") - - chat_history=[] - # 设置按钮的点击事件。当点击时,调用上面定义的 qa_chain_self_answer 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。 - db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs=[ - msg, chatbot], outputs=[msg, chatbot]) - # 创建一个新的gr.Column,用于放置按钮。 - with gr.Column(scale=2): - # 创建三个gr.Button组件,分别设置label参数为"类型1","类型2"和"类型3",设置click参数为不同的函数,比如change_prompt1,change_prompt2和change_prompt3。 - type1_btn = gr.Button("委婉认真") - type2_btn = gr.Button("转换话题") - type3_btn = gr.Button("阴阳怪气") - type1_btn.click(change_prompt1) - type2_btn.click(change_prompt2) - type3_btn.click(change_prompt3) - gr.Markdown("""提醒:
- 1. 初始化数据库时间可能较长,请耐心等待。 - 2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。
- """) -# threads to consume the request -gr.close_all() -# 启动新的 Gradio 应用,设置分享功能为 True,并使用环境变量 PORT1 指定服务器端口。 -# demo.launch(share=True, server_port=int(os.environ['PORT1'])) -# 直接启动 -demo.launch() +os.system('streamlit run web_internlm2.py --server.address=0.0.0.0 --server.port 7860 --server.fileWatcherType none') diff --git a/assets/logo.jpeg b/assets/logo.jpeg new file mode 100644 index 0000000..bdbd026 Binary files /dev/null and b/assets/logo.jpeg differ diff --git a/assets/robot.jpeg b/assets/robot.jpeg new file mode 100644 index 0000000..c7d058d Binary files /dev/null and b/assets/robot.jpeg differ diff --git a/assets/user.png b/assets/user.png new file mode 100644 index 0000000..d0489f2 Binary files /dev/null and b/assets/user.png differ diff --git a/create_db.py b/create_db.py index 47a00df..6ad38cd 100644 --- a/create_db.py +++ b/create_db.py @@ -1,8 +1,7 @@ # 首先导入所需第三方库 from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader -from langchain.document_loaders import PyPDFLoader # for loading the pdf -from langchain.chains import ChatVectorDBChain # for chatting with the pdf +from langchain.document_loaders.csv_loader import CSVLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings @@ -22,7 +21,7 @@ def get_files(dir_path): file_list.append(os.path.join(filepath, filename)) elif filename.endswith(".txt"): file_list.append(os.path.join(filepath, filename)) - elif filename.endswith(".pdf"): + elif filename.endswith(".csv"): file_list.append(os.path.join(filepath, filename)) return file_list @@ -40,8 +39,8 @@ def get_text(dir_path): loader = UnstructuredMarkdownLoader(one_file) elif file_type == 'txt': loader = UnstructuredFileLoader(one_file) - elif file_type == 'pdf': - loader = PyPDFLoader(one_file) + elif file_type == 'csv': + loader = CSVLoader(file_path=one_file) else: # 如果是不符合条件的文件,直接跳过 continue @@ -50,7 +49,13 @@ def get_text(dir_path): # 目标文件夹 tar_dir = [ - "/root/data/paper_demo/graph", + "/root/data/InternLM", + "/root/data/InternLM-XComposer", + "/root/data/lagent", + "/root/data/lmdeploy", + "/root/data/opencompass", + "/root/data/xtuner", + "/root/data/newyear" ] # 加载目标文件 @@ -61,7 +66,7 @@ def get_text(dir_path): # 对文本进行分块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=150) -split_docs = text_splitter.split_documents(docs[:10]) +split_docs = text_splitter.split_documents(docs) # 加载开源词向量模型 embeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer") diff --git a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/data_level0.bin b/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/data_level0.bin deleted file mode 100644 index 33fdee4..0000000 Binary files a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/data_level0.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/header.bin b/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/header.bin deleted file mode 100644 index 2946700..0000000 Binary files a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/header.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/index_metadata.pickle b/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/index_metadata.pickle deleted file mode 100644 index a334db9..0000000 Binary files a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/index_metadata.pickle and /dev/null differ diff --git a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/length.bin b/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/length.bin deleted file mode 100644 index ba2fcb9..0000000 Binary files a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/length.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/link_lists.bin b/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/link_lists.bin deleted file mode 100644 index 1941410..0000000 Binary files a/data_base/vector_db/chroma/a763cd52-c9c6-49bf-bf34-eb86d5c20247/link_lists.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/chroma.sqlite3 b/data_base/vector_db/chroma/chroma.sqlite3 deleted file mode 100644 index 64566c2..0000000 Binary files a/data_base/vector_db/chroma/chroma.sqlite3 and /dev/null differ diff --git a/model/.gitkeep b/model/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/relation/ChinaRelationship.py b/relation/ChinaRelationship.py new file mode 100755 index 0000000..082dd51 --- /dev/null +++ b/relation/ChinaRelationship.py @@ -0,0 +1,80 @@ +import json +import re + + +class RelationshipCounter: + def __init__(self, data_file="data.json", filter_file="filter.json", reverse=False): + self.data = self.load_json(data_file) + self.filter = self.load_json(filter_file)["filter"] + self.reverse = reverse # 是否反转 + + def load_json(self, file_name): + with open(file_name, "r", encoding="utf-8") as f: + return json.load(f) + + # 称谓转换成关联字符 + def transform_title_to_key(self, string): + result = string.replace("的", ",").replace("我", "").replace("爸爸", "f").replace("父亲", "f").replace("妈妈", "m").replace( + "母亲", "m").replace("爷爷", "f,f").replace("奶奶", "f,m").replace("外公", "m,f").replace("姥爷", "m,f").replace("外婆", + "m,m").replace( + "姥姥", "m,m").replace("老公", "h").replace("丈夫", "h").replace("老婆", "w").replace("妻子", "h").replace("儿子", + "s").replace( + "女儿", "d").replace("兄弟", "xd").replace("哥哥", "ob").replace("弟弟", "lb").replace("姐妹", "xs").replace("姐姐", + "os").replace( + "妹妹", "ls").strip(",") + "," + for f in self.filter: + exp = "^" + f["exp"].replace("&", "\\&").replace(",", ".*") + "$" + result = re.sub(exp, f["str"].replace("$", "\\"), result) + if self.reverse: + result = result.replace("f", "m").replace("m", "f") + if result.endswith(","): + result = result[:-1] + return result + + # 错误关系判断 + def error_message(self, key): + if key.find("ob,h") != -1 or key.find("xb,h") != -1 or key.find("lb,h") != -1 or key.find("os,w") != -1 or key.find( + "ls,w") != -1 or key.find("xs,w") != -1 or key.find("f,h") != -1 or key.find("m,w") != -1 or key.find( + "d,w") != -1 or key.find("s,h") != -1: + return "根据我国法律暂不支持同性婚姻,怎么称呼你自己决定吧" + elif key.find("h,h") != -1 or key.find("w,w") != -1: + return "根据我国法律暂不支持重婚,怎么称呼你自己决定吧" + return key + + # 关系链转换成称谓 + def transform_key_to_title(self, string): + if not string: + return None + result = [] + seen = set() + for s in string.split("#"): + if s != self.error_message(s): + return self.error_message(s) + if s in self.data and self.data[s][0] not in seen: + result.append(self.data[s][0]) + seen.add(self.data[s][0]) + # 如果结果为空,再使用逗号分割子字符串 + if not result: + for s in string.split(','): + if s != self.error_message(s): + return self.error_message(s) + if s in self.data and self.data[s][0] not in seen: + result.append(self.data[s][0]) + seen.add(self.data[s][0]) + + if '自己' in result: + result.remove('自己') + result_str = ','.join(result) + if self.reverse: + result_str = result_str.replace("父", "母").replace("母", "父") + return result_str + + +if __name__ == '__main__': + rc = RelationshipCounter() + print(rc.transform_key_to_title(rc.transform_title_to_key("我的爸爸"))) + print(rc.transform_key_to_title(rc.transform_title_to_key("我的父亲的儿子"))) + print(rc.transform_key_to_title(rc.transform_title_to_key("我的哥哥的丈夫"))) + print(rc.transform_key_to_title(rc.transform_title_to_key("我的哥哥的弟弟"))) + print(rc.transform_key_to_title(rc.transform_title_to_key("我的爸爸的爸爸"))) + print(rc.transform_key_to_title(rc.transform_title_to_key("我的哥哥的姐姐的妹妹"))) diff --git a/relation/data.json b/relation/data.json new file mode 100755 index 0000000..f35da38 --- /dev/null +++ b/relation/data.json @@ -0,0 +1,2584 @@ +{ + "": [ + "自己", + "我" + ], + "[s|d]": [ + "子女", + "儿女", + "小孩", + "孩子" + ], + "[f|m]": [ + "父母", + "爹娘", + "爹妈", + "爸妈", + "高堂" + ], + "f": [ + "爸爸", + "父亲", + "阿爸", + "老爸", + "老窦", + "爹", + "爹爹", + "爹地", + "爹啲", + "老爹", + "大大", + "老爷子" + ], + "f,f": [ + "爷爷", + "祖父", + "阿爷", + "奶爷", + "阿公" + ], + "f,f,f": [ + "曾祖父", + "太爷", + "太爷爷", + "太公", + "祖公", + "祖奶爷" + ], + "f,f,f,f": [ + "高祖父", + "老太爷", + "祖太爷", + "祖太爷爷", + "祖太公" + ], + "f,f,f,f,ob": [ + "伯高祖父" + ], + "f,f,f,f,ob,w": [ + "伯高祖母" + ], + "f,f,f,f,lb": [ + "叔高祖父" + ], + "f,f,f,f,lb,w": [ + "叔高祖母" + ], + "f,f,f,f,xs": [ + "姑高祖母" + ], + "f,f,f,f,xs,h": [ + "姑高祖父" + ], + "f,f,f,m": [ + "高祖母", + "老太太", + "祖太太", + "祖太奶", + "祖太奶奶", + "祖太婆" + ], + "f,f,f,m,xs": [ + "姨高祖母" + ], + "f,f,f,m,xs,h": [ + "姨高祖父" + ], + "f,f,f,ob": [ + "曾伯祖父", + "曾伯父", + "伯曾祖父", + "伯公太", + "伯太爷" + ], + "f,f,f,ob,w": [ + "曾伯祖母", + "曾伯母", + "伯曾祖母", + "伯婆太", + "伯太太" + ], + "f,f,f,lb": [ + "曾叔祖父", + "曾叔父", + "叔曾祖父", + "叔公太", + "叔太爷" + ], + "f,f,f,lb,w": [ + "曾叔祖母", + "曾叔母", + "叔曾祖母", + "叔婆太", + "叔太太" + ], + "f,f,f,xb,s&o": [ + "堂伯祖父" + ], + "f,f,f,xb,s&o,w": [ + "堂伯祖母" + ], + "f,f,f,xb,s&l": [ + "堂叔祖父" + ], + "f,f,f,xb,s&l,w": [ + "堂叔祖母" + ], + "f,f,f,xb,s,s&o": [ + "从伯父" + ], + "f,f,f,xb,s,s&o,w": [ + "从伯母" + ], + "f,f,f,xb,s,s&l": [ + "从叔父" + ], + "f,f,f,xb,s,s&l,w": [ + "从叔母" + ], + "f,f,f,xb,s,s,s&o": [ + "族兄" + ], + "f,f,f,xb,s,s,s&l": [ + "族弟" + ], + "f,f,f,xb,d": [ + "堂姑祖母" + ], + "f,f,f,xb,d,h": [ + "堂姑祖父" + ], + "f,f,f,xs": [ + "曾祖姑母", + "姑曾祖母", + "太姑婆", + "姑婆太", + "姑太太" + ], + "f,f,f,xs,h": [ + "曾祖姑丈", + "姑曾祖父", + "太姑丈公", + "姑丈公太", + "姑太爷" + ], + "f,f,f,xs,s&o": [ + "表伯祖父" + ], + "f,f,f,xs,s&o,w": [ + "表伯祖母" + ], + "f,f,f,xs,s&l": [ + "表叔祖父" + ], + "f,f,f,xs,s&l,w": [ + "表叔祖母" + ], + "f,f,f,xs,d": [ + "表姑祖母" + ], + "f,f,f,xs,d,h": [ + "表姑祖父" + ], + "f,f,m": [ + "曾祖母", + "太奶奶", + "太婆", + "祖婆", + "祖奶奶" + ], + "f,f,m,f": [ + "高外祖父", + "祖太姥爷", + "祖太公" + ], + "f,f,m,m": [ + "高外祖母", + "祖太姥姥", + "祖太姥娘", + "祖太婆" + ], + "f,f,m,xb": [ + "舅曾祖父", + "太舅公", + "太舅爷", + "舅太爷", + "舅太爷爷" + ], + "f,f,m,xb,w": [ + "舅曾祖母", + "太舅婆", + "舅太太", + "舅太奶奶" + ], + "f,f,m,xb,s&o": [ + "表伯祖父" + ], + "f,f,m,xb,s&o,w": [ + "表伯祖母" + ], + "f,f,m,xb,s&l": [ + "表叔祖父" + ], + "f,f,m,xb,s&l,w": [ + "表叔祖母" + ], + "f,f,m,xb,d": [ + "表姑祖母" + ], + "f,f,m,xb,d,h": [ + "表姑祖父" + ], + "f,f,m,xs": [ + "姨曾祖母", + "太姨奶", + "姨太太", + "曾姨奶奶", + "姨太奶奶" + ], + "f,f,m,xs,h": [ + "姨曾祖父", + "太姨爷", + "姨太爷", + "姨太爷爷" + ], + "f,f,m,xs,s&o": [ + "表伯祖父" + ], + "f,f,m,xs,s&o,w": [ + "表伯祖母" + ], + "f,f,m,xs,s&l": [ + "表叔祖父" + ], + "f,f,m,xs,s&l,w": [ + "表叔祖母" + ], + "f,f,m,xs,d": [ + "表姑祖母" + ], + "f,f,m,xs,d,h": [ + "表姑祖父" + ], + "f,f,xb": [ + "堂祖父", + "x爷爷" + ], + "f,f,xb,w": [ + "堂祖母" + ], + "f,f,xb,s&o": [ + "堂伯", + "堂伯父", + "从父伯父" + ], + "f,f,xb,s&o,w": [ + "堂伯母", + "从父伯母" + ], + "f,f,xb,s&l": [ + "堂叔", + "从父叔父" + ], + "f,f,xb,s,w": [ + "堂婶", + "堂叔母", + "堂婶母", + "从父叔母" + ], + "f,f,xb,s,s&o": [ + "从兄", + "从兄弟" + ], + "f,f,xb,s,s&o,w": [ + "从嫂" + ], + "f,f,xb,s,s&l": [ + "从弟", + "从兄弟" + ], + "f,f,xb,s,s&l,w": [ + "从弟妹" + ], + "f,f,xb,s,s,s": [ + "从侄", + "从侄子" + ], + "f,f,xb,s,s,s,w": [ + "从侄媳妇" + ], + "f,f,xb,s,s,s,s": [ + "从侄孙" + ], + "f,f,xb,s,s,s,d": [ + "从侄孙女" + ], + "f,f,xb,s,s,d": [ + "从侄女" + ], + "f,f,xb,s,s,d,h": [ + "从侄女婿" + ], + "f,f,xb,s,d&o": [ + "从姐", + "从姐妹" + ], + "f,f,xb,s,d&o,h": [ + "从姐夫" + ], + "f,f,xb,s,d&l": [ + "从妹", + "从姐妹" + ], + "f,f,xb,s,d&l,h": [ + "从妹夫" + ], + "f,f,xb,d": [ + "堂姑", + "堂姑妈", + "堂姑母", + "从父姑母" + ], + "f,f,xb,d,h": [ + "堂姑丈", + "堂姑爸", + "堂姑父", + "从父姑父" + ], + "f,f,xb,d,s&o": [ + "堂姑表兄" + ], + "f,f,xb,d,s&l": [ + "堂姑表弟" + ], + "f,f,xb,d,d&o": [ + "堂姑表姐" + ], + "f,f,xb,d,d&l": [ + "堂姑表妹" + ], + "f,f,ob": [ + "伯祖父", + "伯老爷", + "伯公", + "大爷爷", + "大爷", + "堂祖父", + "伯爷爷" + ], + "f,f,ob,w": [ + "伯祖母", + "伯奶奶", + "伯婆", + "大奶奶", + "堂祖母" + ], + "f,f,lb": [ + "叔祖父", + "叔老爷", + "叔公", + "小爷爷", + "堂祖父", + "叔爷爷", + "叔奶爷" + ], + "f,f,lb,w": [ + "叔祖母", + "叔奶奶", + "叔婆", + "小奶奶", + "堂祖母" + ], + "f,f,xs": [ + "祖姑母", + "姑祖母", + "姑奶奶", + "姑婆" + ], + "f,f,xs,h": [ + "祖姑父", + "姑祖父", + "姑爷爷", + "姑老爷", + "姑公", + "姑奶爷", + "姑丈公" + ], + "f,f,xs,s&o": [ + "姑表伯父", + "表伯父", + "表伯" + ], + "f,f,xs,s&o,w": [ + "姑表伯母", + "表伯母" + ], + "f,f,xs,s&l": [ + "姑表叔父", + "表叔父", + "表叔爸", + "表叔" + ], + "f,f,xs,s&l,w": [ + "姑表叔母", + "表叔母", + "表叔妈", + "表婶" + ], + "f,f,xs,d": [ + "姑表姑母", + "表姑妈", + "表姑母", + "表姑姑", + "表姑" + ], + "f,f,xs,d,h": [ + "姑表姑父", + "表姑爸", + "表姑父", + "表姑丈" + ], + "f,m": [ + "奶奶", + "祖母", + "阿嫲", + "阿嬷", + "嫲嫲" + ], + "f,m,f": [ + "曾外祖父", + "外太公", + "太姥爷" + ], + "f,m,f,f": [ + "祖太爷", + "祖太爷爷", + "祖太公" + ], + "f,m,f,m": [ + "祖太太", + "祖太奶奶", + "祖太婆" + ], + "f,m,f,xb,s": [ + "堂舅祖父" + ], + "f,m,f,xb,s,w": [ + "堂舅祖母" + ], + "f,m,f,xb,d": [ + "堂姨祖母" + ], + "f,m,f,xb,d,h": [ + "堂姨祖父" + ], + "f,m,f,ob": [ + "伯曾外祖父", + "伯太姥爷", + "伯太奶爷" + ], + "f,m,f,ob,w": [ + "伯曾外祖母", + "伯太姥姥", + "伯太奶奶" + ], + "f,m,f,lb": [ + "叔曾外祖父", + "叔太姥爷", + "叔太奶爷" + ], + "f,m,f,lb,w": [ + "叔曾外祖母", + "叔太姥姥", + "叔太奶奶" + ], + "f,m,f,xs": [ + "姑曾外祖母", + "姑太姥姥", + "姑太奶奶" + ], + "f,m,f,xs,h": [ + "姑曾外祖父", + "姑太姥爷", + "姑太奶爷", + "姑太爷爷" + ], + "f,m,f,xs,s": [ + "表舅祖父" + ], + "f,m,f,xs,s,w": [ + "表舅祖母" + ], + "f,m,m": [ + "曾外祖母", + "外太婆", + "太姥姥" + ], + "f,m,m,f": [ + "祖太姥爷", + "祖太公" + ], + "f,m,m,m": [ + "祖太姥姥", + "祖太姥娘", + "祖太婆" + ], + "f,m,m,xb": [ + "舅曾外祖父", + "舅太姥爷", + "舅太奶爷" + ], + "f,m,m,xb,w": [ + "舅曾外祖母", + "舅太姥姥", + "舅太奶奶" + ], + "f,m,m,xb,s": [ + "表舅祖父" + ], + "f,m,m,xb,s,w": [ + "表舅祖母" + ], + "f,m,m,xb,d": [ + "表姨祖母" + ], + "f,m,m,xb,d,h": [ + "表姨祖父" + ], + "f,m,m,xs": [ + "姨曾外祖母", + "姨太姥姥", + "姨太奶奶" + ], + "f,m,m,xs,h": [ + "姨曾外祖父", + "姨太姥爷", + "姨太奶爷" + ], + "f,m,m,xs,d": [ + "表姨祖母" + ], + "f,m,m,xs,d,h": [ + "表姨祖父" + ], + "f,m,m,xs,s": [ + "表舅祖父" + ], + "f,m,m,xs,s,w": [ + "表舅祖母" + ], + "f,m,xb": [ + "舅公", + "舅祖父", + "舅老爷", + "舅爷爷", + "舅爷", + "舅祖", + "舅奶爷", + "太舅父" + ], + "f,m,xb,w": [ + "舅婆", + "舅祖母", + "舅奶奶", + "妗婆", + "太舅母" + ], + "f,m,xb,s&o": [ + "舅表伯父", + "表伯父", + "表伯" + ], + "f,m,xb,s&o,w": [ + "舅表伯母", + "表伯母" + ], + "f,m,xb,s&l": [ + "舅表叔父", + "表叔父", + "表叔爸", + "表叔" + ], + "f,m,xb,s&l,w": [ + "舅表叔母", + "表叔母", + "表叔妈", + "表婶" + ], + "f,m,xb,s,s": [ + "从表兄弟" + ], + "f,m,xb,s,d": [ + "从表姐妹" + ], + "f,m,xb,d": [ + "舅表姑母", + "表姑妈", + "表姑母", + "表姑姑", + "表姑" + ], + "f,m,xb,d,h": [ + "舅表姑父", + "表姑爸", + "表姑父", + "表姑丈" + ], + "f,m,xb,d,s": [ + "从表兄弟" + ], + "f,m,xb,d,d": [ + "从表姐妹" + ], + "f,m,xs": [ + "祖姨母", + "姨祖母", + "姨婆", + "姨奶奶", + "姨奶" + ], + "f,m,xs,h": [ + "祖姨父", + "姨祖父", + "姨公", + "姨爷爷", + "姨丈公", + "姨爷", + "姨老爷", + "姨奶爷" + ], + "f,m,xs,s&o": [ + "姨表伯父", + "表伯", + "表伯父", + "从母伯父" + ], + "f,m,xs,s&o,w": [ + "姨表伯母", + "表伯母", + "从母伯母" + ], + "f,m,xs,s&l": [ + "姨表叔父", + "表叔父", + "表叔爸", + "表叔", + "从母叔父" + ], + "f,m,xs,s&l,w": [ + "姨表叔母", + "表叔母", + "表叔妈", + "表婶", + "从母叔母" + ], + "f,m,xs,s,s": [ + "从表兄弟" + ], + "f,m,xs,s,d": [ + "从表姐妹" + ], + "f,m,xs,d": [ + "姨表姑母", + "表姑妈", + "表姑母", + "表姑姑", + "表姑", + "从母姑母" + ], + "f,m,xs,d,h": [ + "姨表姑父", + "表姑爸", + "表姑父", + "表姑丈", + "从母姑父" + ], + "f,m,xs,d,s": [ + "从表兄弟" + ], + "f,m,xs,d,d": [ + "从表姐妹" + ], + "f,xb,s&o": [ + "堂哥", + "堂兄", + "堂阿哥" + ], + "f,xb,s&o,w": [ + "堂嫂" + ], + "f,xb,s&l": [ + "堂弟", + "堂阿弟" + ], + "f,xb,s&l,w": [ + "堂弟媳", + "堂弟妹" + ], + "f,xb,s,s": [ + "堂侄", + "堂侄子" + ], + "f,xb,s,s,w": [ + "堂侄媳妇" + ], + "f,xb,s,s,s": [ + "堂侄孙" + ], + "f,xb,s,s,s,w": [ + "堂侄孙媳妇" + ], + "f,xb,s,s,d": [ + "堂侄孙女" + ], + "f,xb,s,s,d,h": [ + "堂侄孙女婿" + ], + "f,xb,s,d": [ + "堂侄女" + ], + "f,xb,s,d,h": [ + "堂侄女婿" + ], + "f,xb,d&o": [ + "堂姐", + "堂阿姐" + ], + "f,xb,d&o,h": [ + "堂姐夫" + ], + "f,xb,d&l": [ + "堂妹", + "堂阿妹" + ], + "f,xb,d&l,h": [ + "堂妹夫" + ], + "f,xb,d,s": [ + "堂外甥" + ], + "f,xb,d,d": [ + "堂外甥女" + ], + "f,ob": [ + "伯父", + "伯伯", + "大伯", + "x伯" + ], + "f,ob,w": [ + "伯母", + "大娘", + "大妈", + "x妈" + ], + "f,lb": [ + "叔叔", + "叔父", + "阿叔", + "叔爸", + "叔爹", + "仲父", + "x叔", + "叔" + ], + "f,lb,w": [ + "婶婶", + "婶母", + "阿婶", + "家婶", + "叔母", + "叔妈", + "叔娘", + "季母", + "x婶", + "婶" + ], + "f,xs": [ + "姑妈", + "姑姑", + "姑娘", + "大姑妈", + "x姑妈", + "姑" + ], + "f,xs,h": [ + "姑丈", + "姑父", + "姑爸", + "姑夫" + ], + "f,xs,s&o": [ + "姑表哥", + "表哥" + ], + "f,xs,s&o,w": [ + "姑表嫂", + "表嫂" + ], + "f,xs,s&l": [ + "姑表弟", + "表弟" + ], + "f,xs,s&l,w": [ + "姑表弟媳", + "表弟媳", + "表弟妹" + ], + "f,xs,s,s": [ + "表侄", + "表侄子" + ], + "f,xs,s,s,s": [ + "表侄孙" + ], + "f,xs,s,s,s,w": [ + "表侄孙媳妇" + ], + "f,xs,s,s,d": [ + "表侄孙女" + ], + "f,xs,s,s,d,h": [ + "表侄孙女婿" + ], + "f,xs,s,d": [ + "表侄女" + ], + "f,xs,s,d,s": [ + "外表侄孙" + ], + "f,xs,s,d,s,w": [ + "外表侄孙媳妇" + ], + "f,xs,s,d,d": [ + "外表侄孙女" + ], + "f,xs,s,d,d,h": [ + "外表侄孙女婿" + ], + "f,xs,d&o": [ + "姑表姐", + "表姐" + ], + "f,xs,d&o,h": [ + "姑表姐夫", + "表姐夫", + "表姐丈" + ], + "f,xs,d&l": [ + "姑表妹", + "表妹" + ], + "f,xs,d&l,h": [ + "姑表妹夫", + "表妹夫" + ], + "f,xs,d,s": [ + "表外甥" + ], + "f,xs,d,d": [ + "表外甥女" + ], + "f,os": [ + "姑母" + ], + "f,ls": [ + "姑姐" + ], + "m": [ + "妈妈", + "母亲", + "老妈", + "阿妈", + "老母", + "老妈子", + "娘", + "娘亲", + "妈咪" + ], + "m,f": [ + "外公", + "外祖父", + "姥爷" + ], + "m,f,f": [ + "外曾祖父", + "外太祖父", + "外太公", + "外太爷爷", + "太外祖父" + ], + "m,f,f,f": [ + "外高祖父", + "祖太爷", + "祖太爷爷", + "祖太公" + ], + "m,f,f,m": [ + "外高祖母", + "祖太太", + "祖太奶奶", + "祖太婆" + ], + "m,f,f,xb,s&o": [ + "堂伯外祖父" + ], + "m,f,f,xb,s&o,w": [ + "堂伯外祖母" + ], + "m,f,f,xb,s&l": [ + "堂叔外祖父" + ], + "m,f,f,xb,s&l,w": [ + "堂叔外祖母" + ], + "m,f,f,xb,d": [ + "堂姑外祖母" + ], + "m,f,f,xb,d,h": [ + "堂姑外祖父" + ], + "m,f,f,ob": [ + "伯外曾祖父", + "外太伯公", + "伯太姥爷", + "伯太奶爷", + "伯太爷爷" + ], + "m,f,f,ob,w": [ + "伯外曾祖母", + "外太伯母", + "伯太姥姥", + "伯太奶奶" + ], + "m,f,f,lb": [ + "叔外曾祖父", + "外太叔公", + "叔太姥爷", + "叔太奶爷", + "叔太爷爷" + ], + "m,f,f,lb,w": [ + "叔外曾祖母", + "外太叔母", + "叔太姥姥", + "叔太奶奶" + ], + "m,f,f,xs": [ + "姑外曾祖母", + "外太姑婆", + "姑太姥姥", + "姑太奶奶" + ], + "m,f,f,xs,h": [ + "姑外曾祖父", + "外太姑丈公", + "姑太姥爷", + "姑太奶爷", + "姑太爷爷" + ], + "m,f,f,xs,s&o": [ + "表伯外祖父", + "外表伯祖父" + ], + "m,f,f,xs,s&o,w": [ + "表伯外祖母", + "外表伯祖母" + ], + "m,f,f,xs,s&l": [ + "表叔外祖父", + "外表叔祖父" + ], + "m,f,f,xs,s&l,w": [ + "表叔外祖母", + "外表叔祖母" + ], + "m,f,f,xs,d": [ + "表姑外祖母" + ], + "m,f,f,xs,d,h": [ + "表姑外祖父" + ], + "m,f,m": [ + "外曾祖母", + "外太祖母", + "太外祖母", + "外太奶奶", + "外太婆" + ], + "m,f,m,f": [ + "外高外祖父", + "祖太姥爷", + "祖太公" + ], + "m,f,m,m": [ + "外高外祖母", + "祖太姥姥", + "祖太姥娘", + "祖太婆" + ], + "m,f,m,xb": [ + "舅外曾祖父", + "外太舅公", + "舅太姥爷", + "舅太奶爷" + ], + "m,f,m,xb,w": [ + "舅外曾祖母", + "外太舅母", + "舅太姥姥", + "舅太奶奶", + "外太舅婆" + ], + "m,f,m,xb,d": [ + "表姑外祖母" + ], + "m,f,m,xb,d,h": [ + "表姑外祖父" + ], + "m,f,m,xs": [ + "姨外曾祖母", + "外太姨婆", + "姨太姥姥", + "姨太奶奶" + ], + "m,f,m,xs,h": [ + "姨外曾祖父", + "外太姑姨公", + "姨太姥爷", + "姨太奶爷", + "姨太爷爷" + ], + "m,f,m,xs,d": [ + "表姑外祖母" + ], + "m,f,m,xs,d,h": [ + "表姑外祖父" + ], + "m,f,xb": [ + "大姥爷/小姥爷", + "x姥爷" + ], + "m,f,xb,s": [ + "堂舅", + "堂舅爸", + "堂舅父", + "堂舅舅", + "从父舅父" + ], + "m,f,xb,s,w": [ + "堂舅妈", + "堂舅母", + "从父舅母" + ], + "m,f,xb,s,s&o": [ + "堂舅表兄" + ], + "m,f,xb,s,s&l": [ + "堂舅表弟" + ], + "m,f,xb,s,d&o": [ + "堂舅表姐" + ], + "m,f,xb,s,d&l": [ + "堂舅表妹" + ], + "m,f,xb,d": [ + "堂姨", + "堂姨妈", + "堂姨母", + "从父姨母" + ], + "m,f,xb,d,h": [ + "堂姨丈", + "堂姨爸", + "堂姨父", + "从父姨父" + ], + "m,f,xb,d,s&o": [ + "堂姨表兄" + ], + "m,f,xb,d,s&l": [ + "堂姨表弟" + ], + "m,f,xb,d,d&o": [ + "堂姨表姐" + ], + "m,f,xb,d,d&l": [ + "堂姨表妹" + ], + "m,f,ob": [ + "伯外祖父", + "外伯祖父", + "伯姥爷", + "大姥爷", + "外伯祖", + "伯公" + ], + "m,f,ob,w": [ + "伯外祖母", + "外伯祖母", + "伯姥姥", + "大姥姥", + "外姆婆", + "伯婆" + ], + "m,f,lb": [ + "叔外祖父", + "外叔祖父", + "叔姥爷", + "叔公", + "小姥爷", + "外叔祖", + "叔外祖", + "叔爷爷" + ], + "m,f,lb,w": [ + "叔外祖母", + "外叔祖母", + "叔姥姥", + "叔婆", + "小姥姥", + "外姆婆" + ], + "m,f,xs": [ + "姑外祖母", + "外姑祖母", + "姑姥姥", + "外太姑母", + "姑婆" + ], + "m,f,xs,h": [ + "姑外祖父", + "外姑祖父", + "姑姥爷", + "外太姑父", + "姑公" + ], + "m,f,xs,s": [ + "姑表舅父", + "姑表舅爸", + "表舅父", + "表舅爸", + "表舅", + "表舅舅", + "姑表舅舅" + ], + "m,f,xs,s,w": [ + "姑表舅母", + "姑表舅妈", + "表舅母", + "表舅妈" + ], + "m,f,xs,s,s": [ + "从表兄弟" + ], + "m,f,xs,s,d": [ + "从表姐妹" + ], + "m,f,xs,d": [ + "姑表姨母", + "姑表姨妈", + "表姨母", + "表姨妈", + "表姨", + "表阿姨", + "姑表姨姨" + ], + "m,f,xs,d,h": [ + "姑表姨父", + "姑表姨父", + "表姨丈", + "表姨父" + ], + "m,f,xs,d,s": [ + "从表兄弟" + ], + "m,f,xs,d,d": [ + "从表姐妹" + ], + "m,m": [ + "外婆", + "外祖母", + "姥姥", + "阿婆" + ], + "m,m,f": [ + "外曾外祖父", + "外太外公", + "外太姥爷" + ], + "m,m,f,f": [ + "祖太爷", + "祖太爷爷", + "祖太公" + ], + "m,m,f,m": [ + "祖太太", + "祖太奶奶", + "祖太婆" + ], + "m,m,f,xb,s": [ + "堂舅外祖父" + ], + "m,m,f,xb,s,w": [ + "堂舅外祖母" + ], + "m,m,f,xb,d": [ + "堂姨外祖母" + ], + "m,m,f,xb,d,h": [ + "堂姨外祖父" + ], + "m,m,f,ob": [ + "伯外曾外祖父", + "伯太姥爷" + ], + "m,m,f,ob,w": [ + "伯外曾外祖母", + "伯太姥姥" + ], + "m,m,f,lb": [ + "叔外曾外祖父", + "叔太姥爷" + ], + "m,m,f,lb,w": [ + "叔外曾外祖母", + "叔太姥姥" + ], + "m,m,f,xs": [ + "姑外曾外祖母", + "姑太姥姥" + ], + "m,m,f,xs,h": [ + "姑外曾外祖父", + "姑太姥爷" + ], + "m,m,f,xs,s": [ + "表舅外祖父" + ], + "m,m,f,xs,s,w": [ + "表舅外祖母" + ], + "m,m,f,xs,d": [ + "表姨外祖母" + ], + "m,m,f,xs,d,h": [ + "表姨外祖父" + ], + "m,m,m": [ + "外曾外祖母", + "外太外婆", + "外太姥姥" + ], + "m,m,m,f": [ + "祖太姥爷", + "祖太公" + ], + "m,m,m,m": [ + "祖太姥姥", + "祖太姥娘", + "祖太婆" + ], + "m,m,m,xb": [ + "舅外曾外祖父", + "舅太姥爷" + ], + "m,m,m,xb,w": [ + "舅外曾外祖母", + "舅太姥姥" + ], + "m,m,m,xb,s": [ + "表舅外祖父" + ], + "m,m,m,xb,s,w": [ + "表舅外祖母" + ], + "m,m,m,xb,d": [ + "表姨外祖母" + ], + "m,m,m,xb,d,h": [ + "表姨外祖父" + ], + "m,m,m,xs": [ + "姨外曾外祖母", + "姨太姥姥" + ], + "m,m,m,xs,h": [ + "姨外曾外祖父", + "姨太姥爷" + ], + "m,m,m,xs,s": [ + "表舅外祖父" + ], + "m,m,m,xs,s,w": [ + "表舅外祖母" + ], + "m,m,m,xs,d": [ + "表姨外祖母" + ], + "m,m,m,xs,d,h": [ + "表姨外祖父" + ], + "m,m,xb": [ + "外舅公", + "舅外祖父", + "外舅祖父", + "舅姥爷", + "舅外公", + "舅公", + "x舅姥爷" + ], + "m,m,xb,w": [ + "外舅婆", + "舅外祖母", + "外舅祖母", + "舅姥姥", + "舅婆" + ], + "m,m,xb,s": [ + "舅表舅父", + "舅表舅爸", + "表舅父", + "表舅爸", + "表舅", + "表舅舅", + "舅表舅舅" + ], + "m,m,xb,s,w": [ + "舅表舅母", + "舅表舅妈", + "表舅母", + "表舅妈" + ], + "m,m,xb,s,s": [ + "从表兄弟" + ], + "m,m,xb,s,d": [ + "从表姐妹" + ], + "m,m,xb,d": [ + "舅表姨母", + "舅表姨妈", + "表姨母", + "表姨妈", + "表姨", + "表阿姨", + "舅表姨姨" + ], + "m,m,xb,d,h": [ + "舅表姨父", + "舅表姨丈", + "表姨父", + "表姨丈" + ], + "m,m,xb,d,s": [ + "从表兄弟" + ], + "m,m,xb,d,d": [ + "从表姐妹" + ], + "m,m,xs": [ + "姨外祖母", + "外姨婆", + "外姨祖母", + "姨姥姥", + "姨婆", + "姨姥" + ], + "m,m,xs,h": [ + "姨外祖父", + "外姨丈公", + "外姨祖父", + "姨姥爷", + "姨公" + ], + "m,m,xs,s": [ + "姨表舅父", + "姨表舅爸", + "表舅父", + "表舅爸", + "表舅", + "表舅舅", + "姨表舅舅", + "从母舅父" + ], + "m,m,xs,s,w": [ + "姨表舅母", + "姨表舅妈", + "表舅母", + "表舅妈", + "从母舅母" + ], + "m,m,xs,s,s": [ + "从表兄弟" + ], + "m,m,xs,s,d": [ + "从表姐妹" + ], + "m,m,xs,d": [ + "姨表姨母", + "姨表姨妈", + "表姨母", + "表姨妈", + "表姨", + "表阿姨", + "姨表姨姨", + "从母姨母" + ], + "m,m,xs,d,h": [ + "姨表姨父", + "姨表姨丈", + "表姨父", + "表姨丈", + "从母姨父" + ], + "m,m,xs,d,s": [ + "从表兄弟" + ], + "m,m,xs,d,d": [ + "从表姐妹" + ], + "m,xb": [ + "舅舅", + "舅父", + "舅", + "娘舅", + "舅仔", + "母舅", + "舅爸", + "舅爹", + "阿舅", + "x舅" + ], + "m,xb,w": [ + "舅妈", + "舅母", + "妗子", + "妗妗", + "妗母", + "阿妗", + "x舅妈" + ], + "m,xb,s&o": [ + "舅表哥", + "表哥" + ], + "m,xb,s&o,w": [ + "舅表嫂", + "表嫂" + ], + "m,xb,s&l": [ + "舅表弟", + null, + "表弟" + ], + "m,xb,s&l,w": [ + "舅表弟媳", + "表弟媳", + "表弟妹" + ], + "m,xb,s,s": [ + "表侄", + "表侄子" + ], + "m,xb,s,s,s": [ + "表侄孙" + ], + "m,xb,s,s,s,w": [ + "表侄孙媳妇" + ], + "m,xb,s,s,d": [ + "表侄孙女" + ], + "m,xb,s,s,d,h": [ + "表侄孙女婿" + ], + "m,xb,s,d": [ + "表侄女" + ], + "m,xb,s,d,s": [ + "外表侄孙" + ], + "m,xb,s,d,s,w": [ + "外表侄孙媳妇" + ], + "m,xb,s,d,d": [ + "外表侄孙女" + ], + "m,xb,s,d,d,h": [ + "外表侄孙女婿" + ], + "m,xb,d&o": [ + "舅表姐", + "表姐" + ], + "m,xb,d&o,h": [ + "舅表姐夫", + "表姐夫", + "表姐丈" + ], + "m,xb,d&l": [ + "舅表妹", + "表妹" + ], + "m,xb,d&l,h": [ + "舅表妹夫", + "表妹夫" + ], + "m,xb,d,s": [ + "表外甥" + ], + "m,xb,d,d": [ + "表外甥女" + ], + "m,ob": [ + "大舅" + ], + "m,ob,w": [ + "大舅妈" + ], + "m,lb": [ + "小舅", + "舅父仔" + ], + "m,lb,w": [ + "小舅妈" + ], + "m,xs": [ + "姨妈", + "姨母", + "姨姨", + "姨娘", + "阿姨", + "姨", + "x姨", + "x姨妈" + ], + "m,xs,h": [ + "姨丈", + "姨父", + "姨爸", + "姨爹", + "x姨父" + ], + "m,xs,s&o": [ + "姨表哥", + "表哥" + ], + "m,xs,s&o,w": [ + "姨表嫂", + "表嫂" + ], + "m,xs,s&l": [ + "姨表弟", + "表弟" + ], + "m,xs,s&l,w": [ + "姨表弟媳", + "表弟媳", + "表弟妹" + ], + "m,xs,s,s": [ + "表侄", + "表侄子" + ], + "m,xs,s,s,s": [ + "表侄孙" + ], + "m,xs,s,s,s,w": [ + "表侄孙媳妇" + ], + "m,xs,s,s,d": [ + "表侄孙女" + ], + "m,xs,s,s,d,h": [ + "表侄孙女婿" + ], + "m,xs,s,d": [ + "表侄女" + ], + "m,xs,s,d,s": [ + "外表侄孙" + ], + "m,xs,s,d,s,w": [ + "外表侄孙媳妇" + ], + "m,xs,s,d,d": [ + "外表侄孙女" + ], + "m,xs,s,d,d,h": [ + "外表侄孙女婿" + ], + "m,xs,d&o": [ + "姨表姐", + "表姐" + ], + "m,xs,d&o,h": [ + "姨表姐夫", + "表姐夫", + "表姐丈" + ], + "m,xs,d&l": [ + "姨表妹", + "表妹" + ], + "m,xs,d&l,h": [ + "姨表妹夫", + "表妹夫" + ], + "m,xs,d,s": [ + "表外甥" + ], + "m,xs,d,d": [ + "表外甥女" + ], + "m,os": [ + "大姨", + "大姨妈" + ], + "m,os,h": [ + "大姨父", + "大姨丈" + ], + "m,ls": [ + "小姨", + "小姨妈", + "姨仔" + ], + "m,ls,h": [ + "小姨父", + "小姨丈" + ], + "h": [ + "老公", + "丈夫", + "先生", + "官人", + "男人", + "汉子", + "夫", + "夫君", + "相公", + "夫婿", + "爱人", + "老伴" + ], + "h,f": [ + "公公", + "翁亲", + "老公公" + ], + "h,f,f": [ + "祖翁" + ], + "h,f,f,ob": [ + "伯祖翁" + ], + "h,f,f,ob,w": [ + "伯祖婆" + ], + "h,f,f,lb": [ + "叔祖翁" + ], + "h,f,f,lb,w": [ + "叔祖婆" + ], + "h,f,f,f": [ + "太公翁" + ], + "h,f,f,f,ob": [ + "太伯翁" + ], + "h,f,f,f,ob,w": [ + "太姆婆" + ], + "h,f,f,f,lb": [ + "太叔翁" + ], + "h,f,f,f,lb,w": [ + "太婶婆" + ], + "h,f,f,m": [ + "太奶亲" + ], + "h,f,m": [ + "祖婆" + ], + "h,f,ob": [ + "伯翁" + ], + "h,f,ob,w": [ + "伯婆" + ], + "h,f,lb": [ + "叔公", + "叔翁", + "叔祖" + ], + "h,f,lb,w": [ + "叔婆", + "婶婆" + ], + "h,f,xb,s&o": [ + "堂大伯", + "堂兄", + "堂大伯哥" + ], + "h,f,xb,s&o,w": [ + "堂嫂", + "堂大伯嫂" + ], + "h,f,xb,s&l": [ + "堂叔仔", + "堂弟", + "堂小叔弟" + ], + "h,f,xb,s&l,w": [ + "堂小弟", + "堂弟妇", + "堂小叔弟妇" + ], + "h,f,xb,s,s": [ + "堂夫侄男" + ], + "h,f,xb,s,d": [ + "堂夫侄女" + ], + "h,f,xb,d&o": [ + "堂大姑姐" + ], + "h,f,xb,d&o,h": [ + "堂大姑姐夫" + ], + "h,f,xb,d&l": [ + "堂小姑妹" + ], + "h,f,xb,d&l,h": [ + "堂小姑妹夫" + ], + "h,f,xb,d,s": [ + "堂夫甥男" + ], + "h,f,xb,d,d": [ + "堂夫甥女" + ], + "h,f,xs": [ + "姑婆" + ], + "h,f,xs,h": [ + "姑公" + ], + "h,f,xs,s&o": [ + "姑表大伯子" + ], + "h,f,xs,s&o,w": [ + "姑表大伯嫂" + ], + "h,f,xs,s&l": [ + "姑表小叔弟" + ], + "h,f,xs,s&l,w": [ + "姑表小叔弟妇" + ], + "h,f,xs,d&o": [ + "姑表大姑姐" + ], + "h,f,xs,d&o,h": [ + "姑表大姑姐夫" + ], + "h,f,xs,d&l": [ + "姑表小姑妹" + ], + "h,f,xs,d&l,h": [ + "姑表小姑妹夫" + ], + "h,m": [ + "婆婆", + "姑亲", + "老婆婆" + ], + "h,m,xb": [ + "舅公" + ], + "h,m,xb,w": [ + "舅婆" + ], + "h,m,xs": [ + "姨婆" + ], + "h,m,xs,h": [ + "姨公" + ], + "h,m,xs,s&o": [ + "姨表大伯子" + ], + "h,m,xs,s&o,w": [ + "姨表大伯嫂" + ], + "h,m,xs,s&l": [ + "姨表小叔弟" + ], + "h,m,xs,s&l,w": [ + "姨表小叔弟妇" + ], + "h,m,xs,s,s": [ + "姨表夫侄男" + ], + "h,m,xs,s,d": [ + "姨表夫侄女" + ], + "h,m,xs,d&o": [ + "姨表大姑姐" + ], + "h,m,xs,d&o,h": [ + "姨表大姑姐夫" + ], + "h,m,xs,d&l": [ + "姨表小姑妹" + ], + "h,m,xs,d&l,h": [ + "姨表小姑妹夫" + ], + "h,m,xs,d,s": [ + "姨表夫甥男" + ], + "h,m,xs,d,d": [ + "姨表夫甥女" + ], + "h,ob": [ + "大伯子", + "大伯哥", + "大伯兄", + "夫兄" + ], + "h,ob,w": [ + "大婶子", + "大伯嫂", + "大伯妇", + "伯娘", + "大伯娘", + "大嫂", + "夫兄嫂", + "妯娌" + ], + "h,lb": [ + "小叔子", + "小叔弟" + ], + "h,lb,w": [ + "小婶子", + "小叔妇", + "小叔媳妇", + "小叔弟妇", + "妯娌" + ], + "h,xb,s": [ + "婆家侄" + ], + "h,os": [ + "大姑子", + "大姑", + "大娘姑", + "大姑姊" + ], + "h,os,h": [ + "大姑夫", + "姊丈", + "大姑姐夫", + "大姑姊夫" + ], + "h,ls": [ + "小姑子", + "小姑", + "小姑妹", + "姑仔" + ], + "h,ls,h": [ + "小姑夫", + "小亘子", + "小姑妹夫" + ], + "h,xs,s": [ + "婆家甥" + ], + "w": [ + "老婆", + "妻子", + "太太", + "媳妇儿", + "媳妇", + "夫人", + "女人", + "婆娘", + "妻", + "内人", + "娘子", + "爱人", + "老伴" + ], + "w,f": [ + "岳父", + "岳丈", + "老丈人", + "丈人", + "泰山", + "妻父" + ], + "w,f,f": [ + "太岳父" + ], + "w,f,f,ob": [ + "太伯岳" + ], + "w,f,f,ob,w": [ + "太伯岳母" + ], + "w,f,f,lb,": [ + "太叔岳" + ], + "w,f,f,lb,w": [ + "太叔岳母" + ], + "w,f,f,xb,s&o": [ + "姻伯" + ], + "w,f,f,xb,s&o,w": [ + "姻姆" + ], + "w,f,f,xb,s&l": [ + "姻叔" + ], + "w,f,f,xb,s&l,w": [ + "姻婶" + ], + "w,f,f,xs": [ + "太姑岳母" + ], + "w,f,f,xs,h": [ + "太姑岳父" + ], + "w,f,m": [ + "太岳母" + ], + "w,f,m,xb": [ + "太舅岳父" + ], + "w,f,m,xb,w": [ + "太舅岳母" + ], + "w,f,m,xs": [ + "太姨岳母" + ], + "w,f,m,xs,h": [ + "太姨岳父" + ], + "w,f,xb,s&o": [ + "堂大舅", + "姻家兄" + ], + "w,f,xb,s&l": [ + "堂舅仔", + "姻家弟" + ], + "w,f,xb,d&o": [ + "堂大姨" + ], + "w,f,xb,d&l": [ + "堂姨仔" + ], + "w,f,ob": [ + "伯岳", + "伯岳父" + ], + "w,f,ob,w": [ + "伯岳母" + ], + "w,f,lb": [ + "叔岳", + "叔岳父" + ], + "w,f,lb,w": [ + "叔岳母" + ], + "w,f,xs": [ + "姑岳母" + ], + "w,f,xs,s&o": [ + "表大舅" + ], + "w,f,xs,s&l": [ + "表舅仔" + ], + "w,f,xs,d&o": [ + "表大姨" + ], + "w,f,xs,d&l": [ + "表姨仔" + ], + "w,m": [ + "岳母", + "丈母娘", + "丈母", + "泰水" + ], + "w,m,f": [ + "外太岳父" + ], + "w,m,m": [ + "外太岳母" + ], + "w,m,xb": [ + "舅岳父" + ], + "w,m,xb,w": [ + "舅岳母" + ], + "w,m,xb,s&o": [ + "表大舅" + ], + "w,m,xb,s&l": [ + "表舅仔" + ], + "w,m,xb,d&o": [ + "表大姨" + ], + "w,m,xb,d&l": [ + "表姨仔" + ], + "w,m,xs": [ + "姨岳母" + ], + "w,m,xs,h": [ + "姨岳父" + ], + "w,m,xs,s&o": [ + "表大舅" + ], + "w,m,xs,s&l": [ + "表舅仔" + ], + "w,m,xs,d&o": [ + "表大姨" + ], + "w,m,xs,d&l": [ + "表姨仔" + ], + "w,xb,s": [ + "内侄", + "妻侄" + ], + "w,xb,s,w": [ + "内侄媳妇" + ], + "w,xb,s,s": [ + "侄孙" + ], + "w,xb,s,s,w": [ + "侄孙媳妇" + ], + "w,xb,s,d": [ + "侄孙女" + ], + "w,xb,s,d,h": [ + "侄孙女婿" + ], + "w,xb,d": [ + "内侄女", + "妻侄女" + ], + "w,xb,d,h": [ + "内侄女婿" + ], + "w,xb,d,s": [ + "外侄孙" + ], + "w,xb,d,s,w": [ + "外侄孙媳妇" + ], + "w,xb,d,d": [ + "外侄孙女" + ], + "w,xb,d,d,h": [ + "外侄孙女婿" + ], + "w,ob": [ + "大舅子", + "大舅哥", + "大舅兄", + "内兄", + "妻兄", + "妻舅" + ], + "w,ob,w": [ + "舅嫂", + "大舅妇", + "大舅嫂", + "大舅媳妇", + "大妗子", + "内嫂", + "妻兄嫂" + ], + "w,lb": [ + "小舅子", + "小舅弟", + "内弟", + "妻弟", + "妻舅" + ], + "w,lb,w": [ + "舅弟媳", + "小舅妇", + "小舅弟妇", + "小舅媳妇", + "小妗子", + "妻妹夫" + ], + "w,xs,s": [ + "内甥", + "姨甥", + "妻外甥" + ], + "w,xs,s,w": [ + "姨甥媳妇" + ], + "w,xs,s,s": [ + "姨甥孙" + ], + "w,xs,s,s,w": [ + "姨甥孙媳妇" + ], + "w,xs,s,d": [ + "姨甥孙女" + ], + "w,xs,s,d,h": [ + "姨甥孙女婿" + ], + "w,xs,d": [ + "姨甥女", + "妻外甥女" + ], + "w,xs,d,h": [ + "姨甥女婿" + ], + "w,xs,d,s": [ + "姨甥孙" + ], + "w,xs,d,s,w": [ + "姨甥孙媳妇" + ], + "w,xs,d,d": [ + "姨甥孙女" + ], + "w,xs,d,d,h": [ + "姨甥孙女婿" + ], + "w,os": [ + "大姨子", + "大姨姐", + "大姨姊", + "妻姐" + ], + "w,os,h": [ + "大姨夫", + "大姨姐夫", + "大姨姊夫", + "襟兄", + "连襟", + "姨夫" + ], + "w,ls": [ + "小姨子", + "小姨姐", + "妻妹", + "小妹儿" + ], + "w,ls,h": [ + "小姨夫", + "小姨妹夫", + "襟弟", + "连襟", + "姨夫" + ], + "xb": [ + "兄弟" + ], + "xb,w,f": [ + "姻世伯", + "亲家爷", + "亲爹", + "亲伯" + ], + "xb,w,m": [ + "姻伯母", + "亲家娘", + "亲娘" + ], + "xb,w,xb": [ + "姻兄/姻弟" + ], + "xb,s": [ + "侄子", + "侄儿", + "阿侄" + ], + "xb,s,w": [ + "侄媳", + "侄媳妇" + ], + "xb,s,s": [ + "侄孙", + "侄孙子" + ], + "xb,s,s,w": [ + "侄孙媳" + ], + "xb,s,s,s": [ + "侄曾孙" + ], + "xb,s,s,d": [ + "侄曾孙女" + ], + "xb,s,d": [ + "侄孙女" + ], + "xb,s,d,h": [ + "侄孙女婿" + ], + "xb,d": [ + "侄女", + "侄囡" + ], + "xb,d,h": [ + "侄女婿", + "侄婿" + ], + "xb,d,s": [ + "外侄孙", + "外侄孙子" + ], + "xb,d,s,w": [ + "外侄孙媳妇" + ], + "xb,d,d": [ + "外侄孙女" + ], + "xb,d,d,h": [ + "外侄孙女婿" + ], + "ob": [ + "哥哥", + "哥", + "兄", + "阿哥", + "大哥", + "大佬", + "老哥", + "兄长" + ], + "ob,w": [ + "嫂子", + "大嫂", + "x嫂", + "嫂", + "嫂嫂", + "阿嫂" + ], + "ob,w,f": [ + "姻伯父" + ], + "ob,w,m": [ + "姻伯母" + ], + "lb": [ + "弟弟", + "弟", + "细佬", + "老弟" + ], + "lb,w": [ + "弟妹", + "弟媳", + "弟媳妇" + ], + "lb,w,f": [ + "姻叔父" + ], + "lb,w,m": [ + "姻叔母" + ], + "xs": [ + "姐妹", + "姊妹" + ], + "xs,h,f": [ + "姻世伯", + "亲家爷", + "亲爹", + "亲伯" + ], + "xs,h,m": [ + "姻伯母", + "亲家娘", + "亲娘" + ], + "xs,h,xb": [ + "姻兄/姻弟" + ], + "xs,s": [ + "外甥", + "甥男", + "甥儿", + "甥子", + "外甥儿", + "外甥子", + "外甥儿子" + ], + "xs,s,w": [ + "外甥媳妇" + ], + "xs,s,s": [ + "外甥孙", + "甥孙男", + "甥孙" + ], + "xs,s,s,w": [ + "外甥孙媳妇" + ], + "xs,s,s,s": [ + "外曾甥孙" + ], + "xs,s,s,d": [ + "外曾甥孙女" + ], + "xs,s,d": [ + "外甥孙女", + "甥孙女", + "甥孙" + ], + "xs,s,d,h": [ + "外甥孙女婿" + ], + "xs,s,d,s": [ + "外曾甥孙" + ], + "xs,s,d,d": [ + "外曾甥孙女" + ], + "xs,d": [ + "外甥女", + "外甥囡" + ], + "xs,d,h": [ + "外甥女婿" + ], + "xs,d,s": [ + "外甥孙", + "甥孙男", + "甥孙" + ], + "xs,d,s,w": [ + "外甥孙媳妇" + ], + "xs,d,s,s": [ + "外曾甥孙" + ], + "xs,d,s,d": [ + "外曾甥孙女" + ], + "xs,d,d": [ + "外甥孙女", + "甥孙女", + "甥孙" + ], + "xs,d,d,h": [ + "外甥孙女婿" + ], + "xs,d,d,s": [ + "外曾甥孙" + ], + "xs,d,d,d": [ + "外曾甥孙女" + ], + "os": [ + "姐姐", + "姐", + "家姐", + "阿姐", + "阿姊" + ], + "os,h": [ + "姐夫", + "姊夫", + "姊婿" + ], + "ls": [ + "妹妹", + "妹", + "老妹" + ], + "ls,h": [ + "妹夫", + "妹婿" + ], + "s": [ + "儿子", + "仔", + "阿仔", + "仔仔" + ], + "s,w": [ + "儿媳妇", + "儿媳", + "新妇" + ], + "s,w,xb": [ + "姻侄" + ], + "s,w,xs": [ + "姻侄女" + ], + "s,s": [ + "孙子", + "孙儿", + "孙" + ], + "s,s,w": [ + "孙媳妇", + "孙媳" + ], + "s,s,s": [ + "曾孙" + ], + "s,s,s,w": [ + "曾孙媳妇" + ], + "s,s,s,s": [ + "玄孙", + "元孙", + "膀孙" + ], + "s,s,s,d": [ + "玄孙女" + ], + "s,s,s,s,s": [ + "来孙" + ], + "s,s,d": [ + "曾孙女" + ], + "s,s,d,h": [ + "曾孙女婿" + ], + "s,s,d,s": [ + "外玄孙" + ], + "s,s,d,d": [ + "外玄孙女" + ], + "s,d": [ + "孙女", + "孙囡" + ], + "s,d,h": [ + "孙女婿" + ], + "s,d,s": [ + "曾外孙" + ], + "s,d,d": [ + "曾外孙女" + ], + "d": [ + "女儿", + "千金", + "闺女", + "女", + "阿女", + "女女", + "掌上明珠", + "乖囡", + "囡囡" + ], + "d,h": [ + "女婿", + "姑爷", + "女婿子", + "女婿儿" + ], + "d,h,xb": [ + "姻侄" + ], + "d,h,xs": [ + "姻侄女" + ], + "d,s": [ + "外孙" + ], + "d,s,w": [ + "外孙媳" + ], + "d,s,s": [ + "外曾孙", + "重外孙" + ], + "d,s,d": [ + "外曾孙女", + "重外孙女" + ], + "d,d": [ + "外孙女", + "外孙囡" + ], + "d,d,h": [ + "外孙女婿" + ], + "d,d,s": [ + "外曾外孙" + ], + "d,d,d": [ + "外曾外孙女" + ], + "s,w,m": [ + "亲家母", + "亲家" + ], + "s,w,f": [ + "亲家公", + "亲家翁", + "亲家" + ], + "s,w,f,f": [ + "太姻翁" + ], + "s,w,f,m": [ + "太姻姆" + ], + "s,w,f,ob": [ + "姻兄" + ], + "s,w,f,lb": [ + "姻弟" + ], + "d,h,m": [ + "亲家母", + "亲家" + ], + "d,h,f": [ + "亲家公", + "亲家翁", + "亲家" + ], + "d,h,f,f": [ + "太姻翁" + ], + "d,h,f,m": [ + "太姻姆" + ], + "d,h,f,ob": [ + "姻兄" + ], + "d,h,f,lb": [ + "姻弟" + ] +} \ No newline at end of file diff --git a/relation/filter.json b/relation/filter.json new file mode 100755 index 0000000..b857ecf --- /dev/null +++ b/relation/filter.json @@ -0,0 +1,197 @@ +{ + "filter": [ + { + "exp": "m,h", + "str": "f" + }, + { + "exp": "f,w", + "str": "m" + }, + { + "exp": "os,ls,os", + "str": "os,ls" + }, + { + "exp": "ls,os,ls", + "str": "ls,os" + }, + { + "exp": "ob,lb,ob", + "str": "ob,lb" + }, + { + "exp": "lb,ob,lb", + "str": "lb,ob" + }, + { + "exp": "f,s,f", + "str": "f" + }, + { + "exp": "f,d,f", + "str": "f" + }, + { + "exp": "m,s,m", + "str": "m" + }, + { + "exp": "m,d,m", + "str": "m" + }, + { + "exp": "^(.+)&o([^#]+)&l", + "str": "$1$2" + }, + { + "exp": "^(.+)&l([^#]+)&o", + "str": "$1$2" + }, + { + "exp": "(,[ds],(.+),[ds])&[ol]", + "str": "$1" + }, + { + "exp": "m,h", + "str": "f" + }, + { + "exp": "f,w", + "str": "m" + }, + { + "exp": ",[xol][sb](,[mf])", + "str": "$1" + }, + { + "exp": ",[mf],d&([ol])", + "str": ",$1s" + }, + { + "exp": ",[mf],s&([ol])", + "str": ",$1b" + }, + { + "exp": "^(.*)(,[fh1]|[xol]b),[mf],s(.*)$", + "str": "$1$2,xb$3#$1$2$3" + }, + { + "exp": "^(.*)(,[mw0]|[xol]s),[mf],d(.*)$", + "str": "$1$2,xs$3#$1$2$3" + }, + { + "exp": "(,[mw0]|[xol]s),[mf],s", + "str": "$1,xb" + }, + { + "exp": "(,[fh1]|[xol]b),[mf],d", + "str": "$1,xs" + }, + { + "exp": "^,[mf],s(.*)?$", + "str": ",1$1#,xb$1" + }, + { + "exp": "^,[mf],d(.*)?$", + "str": ",0$1#,xs$1" + }, + { + "exp": "(,o[sb])+(,o[sb])", + "str": "$2" + }, + { + "exp": "(,l[sb])+(,l[sb])", + "str": "$2" + }, + { + "exp": "^(.*)(,[fh1])(,[olx][sb])+,[olx]b(.*)$", + "str": "$1$2,xb$4#$1$2$4" + }, + { + "exp": "^(.*)(,[mw0])(,[olx][sb])+,[olx]s(.*)$", + "str": "$1$2,xs$4#$1$2$4" + }, + { + "exp": "(,[fh1])(,[olx][sb])+,[olx]s", + "str": "$1,xs" + }, + { + "exp": "(,[mw0])(,[olx][sb])+,[olx]b", + "str": "$1,xb" + }, + { + "exp": "^,[olx][sb],[olx]b(.*)?$", + "str": "$1#,xb$1" + }, + { + "exp": "^,[olx][sb],[olx]s(.*)?$", + "str": "$1#,xs$1" + }, + { + "exp": "^,x([sb])$", + "str": ",o$1#,l$1" + }, + { + "exp": ",[ds]&o,ob", + "str": ",s&o" + }, + { + "exp": ",[ds]&o,os", + "str": ",d&o" + }, + { + "exp": ",[ds]&l,lb", + "str": ",s&l" + }, + { + "exp": ",[ds]&l,ls", + "str": ",d&l" + }, + { + "exp": ",[ds](&[ol])?,[olx]s", + "str": ",d" + }, + { + "exp": ",[ds](&[ol])?,[olx]b", + "str": ",s" + }, + { + "exp": "(,[mwd0](&[ol])?|[olx]s),[ds](&[ol])?,m", + "str": "$1" + }, + { + "exp": "(,[mwd0](&[ol])?|[olx]s),[ds](&[ol])?,f", + "str": "$1,h" + }, + { + "exp": "(,[fhs1](&[ol])?|[olx]b),[ds](&[ol])?,f", + "str": "$1" + }, + { + "exp": "(,[fhs1](&[ol])?|[olx]b),[ds](&[ol])?,m", + "str": "$1,w" + }, + { + "exp": "^,[ds],m(.*)?$", + "str": "$1#,w$1" + }, + { + "exp": "^,[ds],f(.*)?$", + "str": "$1#,h$1" + }, + { + "exp": ",[wh](,[ds])", + "str": "$1" + }, + { + "exp": ",w,h|,h,w", + "str": "" + }, + { + "exp": "(.+)?\\[(.+)\\|(.+)\\](.+)?", + "str": "$1$2$4#$1$3$4" + } + + ] +} \ No newline at end of file diff --git a/web_demo.py b/web_demo.py new file mode 100644 index 0000000..6707ee3 --- /dev/null +++ b/web_demo.py @@ -0,0 +1,150 @@ +from langchain.vectorstores import Chroma +from langchain.embeddings.huggingface import HuggingFaceEmbeddings +import os +from LLM import InternLM_LLM +from langchain.chains import RetrievalQA +from langchain import PromptTemplate, LLMChain +from langchain.chains import ConversationalRetrievalChain +from langchain.chains.question_answering import load_qa_chain +from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT +#from langchain.memory import ConversationBufferMemory +# 定义change_prompt1,change_prompt2和change_prompt3函数,分别接受一个参数,比如prompt_qa,用于修改global +prompt_template_qa="""直接回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 + + 问题: {question} + 内容:{context} + 回答:""" +def change_prompt1(*args): + global prompt_template_qa + prompt_template_qa = """委婉认真地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 + 问题: {question} + 内容:{context} + 回答:""" + +def change_prompt2(*args): + global prompt_template_qa + prompt_template_qa = """转移话题地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 + 问题: {question} + 内容:{context} + 回答:""" + +def change_prompt3(*args): + global prompt_template_qa + prompt_template_qa = """阴阳怪气地回答问题,除非问题涉及结婚、生孩、工作、恋爱、买房这些内容时才参考使用内容后内容做出回答。 + 问题: {question} + 内容:{context} + 回答:""" +def load_chain(): + # 加载问答链 + # 定义 Embeddings + embeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer") + + # 向量数据库持久化路径 + persist_directory = 'data_base/vector_db/chroma' + + # 加载数据库 + vectordb = Chroma( + persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上 + embedding_function=embeddings + ) + + # 加载自定义 LLM + llm = InternLM_LLM(model_path = "/root/data/model/Shanghai_AI_Laboratory/internlm-chat-7b") + + # 定义一个 Prompt Template + template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language. + Chat History: {chat_history} + Follow Up Input: {question} + Standalone question: """ + prompt_qg = PromptTemplate( + template=template, + input_variables=["chat_history", "question"], + ) + global prompt_template_qa + + prompt_qa = PromptTemplate( + template=prompt_template_qa, + input_variables=["context", "question"] + ) + question_generator = LLMChain(llm=llm, prompt=prompt_qg) + doc_chain = load_qa_chain(llm=llm, chain_type="stuff", prompt=prompt_qa) + + # 运行 chain + qa_chain = ConversationalRetrievalChain(retriever=vectordb.as_retriever(),question_generator=question_generator,combine_docs_chain=doc_chain,) + + return qa_chain +class Model_center(): + """ + 存储检索问答链的对象 + """ + def __init__(self): + # 构造函数,加载检索问答链 + self.chain = load_chain() + + def qa_chain_self_answer(self, question: str, chat_history:list): + """ + 调用问答链进行回答 + """ + chat_history_tuples = [] + #for message in chat_history: + #chat_history_tuples.append((message[0], message[1])) + chat_history_tuples = tuple(tuple(x) for x in chat_history) + if question == None or len(question) < 1: + return "", chat_history + try: + chat_history.append( + (question, self.chain({"question": question, "chat_history": chat_history_tuples})["answer"])) + # 将问答结果直接附加到问答历史中,Gradio 会将其展示出来 + return "", chat_history + except Exception as e: + return e, chat_history +import gradio as gr + +# 实例化核心功能对象 +model_center = Model_center() +# 创建一个 Web 界面 +block = gr.Blocks() +with block as demo: + with gr.Row(equal_height=True): + with gr.Column(scale=15): + # 展示的页面标题 + gr.Markdown("""

InternLM

+
书生浦语
+ """) + + with gr.Row(): + with gr.Column(scale=4): + # 创建一个聊天机器人对象 + chatbot = gr.Chatbot(height=450, show_copy_button=True) + # 创建一个文本框组件,用于输入 prompt。 + msg = gr.Textbox(label="Prompt/问题") + + with gr.Row(): + # 创建提交按钮。 + db_wo_his_btn = gr.Button("Chat") + with gr.Row(): + # 创建一个清除按钮,用于清除聊天机器人组件的内容。 + clear = gr.ClearButton( + components=[chatbot], value="Clear console") + chat_history=[] + # 设置按钮的点击事件。当点击时,调用上面定义的 qa_chain_self_answer 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。 + db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs=[ + msg, chatbot], outputs=[msg, chatbot]) + # 创建一个新的gr.Column,用于放置按钮。 + with gr.Column(scale=2): + # 创建三个gr.Button组件,分别设置label参数为"类型1","类型2"和"类型3",设置click参数为不同的函数,比如change_prompt1,change_prompt2和change_prompt3。 + type1_btn = gr.Button("委婉认真") + type2_btn = gr.Button("转换话题") + type3_btn = gr.Button("阴阳怪气") + type1_btn.click(change_prompt1) + type2_btn.click(change_prompt2) + type3_btn.click(change_prompt3) + gr.Markdown("""提醒:
+ 1. 初始化数据库时间可能较长,请耐心等待。 + 2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。
+ """) +gr.close_all() +# 直接启动 +demo.launch() + + diff --git a/web_internlm2.py b/web_internlm2.py new file mode 100644 index 0000000..0fd5bf7 --- /dev/null +++ b/web_internlm2.py @@ -0,0 +1,283 @@ +""" +This script refers to the dialogue example of streamlit, the interactive generation code of chatglm2 and transformers. +We mainly modified part of the code logic to adapt to the generation of our model. +Please refer to these links below for more information: + 1. streamlit chat example: https://docs.streamlit.io/knowledge-base/tutorials/build-conversational-apps + 2. chatglm2: https://github.com/THUDM/ChatGLM2-6B + 3. transformers: https://github.com/huggingface/transformers +Please run with the command `streamlit run path/to/web_demo.py --server.address=0.0.0.0 --server.port 7860`. +Using `python path/to/web_demo.py` may cause unknown problems. +""" +import copy +import warnings +from dataclasses import asdict, dataclass +from typing import Callable, List, Optional + +import streamlit as st +import torch +from torch import nn +from transformers.generation.utils import LogitsProcessorList, StoppingCriteriaList +from transformers.utils import logging + +from transformers import AutoTokenizer, AutoModelForCausalLM # isort: skip +from modelscope.hub.snapshot_download import snapshot_download +import os + +# 检查本地是否有对应的权重文件夹 +model_dir_path = os.path.join(os.getcwd(), 'model', 'binbeing', 'SpringFestQA') + +if not os.path.exists(model_dir_path): + model_dir = snapshot_download('binbeing/SpringFestQA', cache_dir='./model') +else: + model_dir = model_dir_path + +logger = logging.get_logger(__name__) + + +@dataclass +class GenerationConfig: + # this config is used for chat to provide more diversity + max_length: int = 32768 + top_p: float = 0.8 + temperature: float = 0.8 + do_sample: bool = True + repetition_penalty: float = 1.005 + + +@torch.inference_mode() +def generate_interactive( + model, + tokenizer, + prompt, + generation_config: Optional[GenerationConfig] = None, + logits_processor: Optional[LogitsProcessorList] = None, + stopping_criteria: Optional[StoppingCriteriaList] = None, + prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor], List[int]]] = None, + additional_eos_token_id: Optional[int] = None, + **kwargs, +): + inputs = tokenizer([prompt], padding=True, return_tensors="pt") + input_length = len(inputs["input_ids"][0]) + for k, v in inputs.items(): + inputs[k] = v.cuda() + input_ids = inputs["input_ids"] + batch_size, input_ids_seq_length = input_ids.shape[0], input_ids.shape[-1] # noqa: F841 # pylint: disable=W0612 + if generation_config is None: + generation_config = model.generation_config + generation_config = copy.deepcopy(generation_config) + model_kwargs = generation_config.update(**kwargs) + bos_token_id, eos_token_id = ( # noqa: F841 # pylint: disable=W0612 + generation_config.bos_token_id, + generation_config.eos_token_id, + ) + if isinstance(eos_token_id, int): + eos_token_id = [eos_token_id] + if additional_eos_token_id is not None: + eos_token_id.append(additional_eos_token_id) + has_default_max_length = kwargs.get("max_length") is None and generation_config.max_length is not None + if has_default_max_length and generation_config.max_new_tokens is None: + warnings.warn( + f"Using `max_length`'s default ({generation_config.max_length}) to control the generation length. " + "This behaviour is deprecated and will be removed from the config in v5 of Transformers -- we" + " recommend using `max_new_tokens` to control the maximum length of the generation.", + UserWarning, + ) + elif generation_config.max_new_tokens is not None: + generation_config.max_length = generation_config.max_new_tokens + input_ids_seq_length + if not has_default_max_length: + logger.warn( # pylint: disable=W4902 + f"Both `max_new_tokens` (={generation_config.max_new_tokens}) and `max_length`(=" + f"{generation_config.max_length}) seem to have been set. `max_new_tokens` will take precedence. " + "Please refer to the documentation for more information. " + "(https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)", + UserWarning, + ) + + if input_ids_seq_length >= generation_config.max_length: + input_ids_string = "input_ids" + logger.warning( + f"Input length of {input_ids_string} is {input_ids_seq_length}, but `max_length` is set to" + f" {generation_config.max_length}. This can lead to unexpected behavior. You should consider" + " increasing `max_new_tokens`." + ) + + # 2. Set generation parameters if not already defined + logits_processor = logits_processor if logits_processor is not None else LogitsProcessorList() + stopping_criteria = stopping_criteria if stopping_criteria is not None else StoppingCriteriaList() + + logits_processor = model._get_logits_processor( + generation_config=generation_config, + input_ids_seq_length=input_ids_seq_length, + encoder_input_ids=input_ids, + prefix_allowed_tokens_fn=prefix_allowed_tokens_fn, + logits_processor=logits_processor, + ) + + stopping_criteria = model._get_stopping_criteria( + generation_config=generation_config, stopping_criteria=stopping_criteria + ) + logits_warper = model._get_logits_warper(generation_config) + + unfinished_sequences = input_ids.new(input_ids.shape[0]).fill_(1) + scores = None + while True: + model_inputs = model.prepare_inputs_for_generation(input_ids, **model_kwargs) + # forward pass to get next token + outputs = model( + **model_inputs, + return_dict=True, + output_attentions=False, + output_hidden_states=False, + ) + + next_token_logits = outputs.logits[:, -1, :] + + # pre-process distribution + next_token_scores = logits_processor(input_ids, next_token_logits) + next_token_scores = logits_warper(input_ids, next_token_scores) + + # sample + probs = nn.functional.softmax(next_token_scores, dim=-1) + if generation_config.do_sample: + next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1) + else: + next_tokens = torch.argmax(probs, dim=-1) + + # update generated ids, model inputs, and length for next step + input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1) + model_kwargs = model._update_model_kwargs_for_generation(outputs, model_kwargs, is_encoder_decoder=False) + unfinished_sequences = unfinished_sequences.mul((min(next_tokens != i for i in eos_token_id)).long()) + + output_token_ids = input_ids[0].cpu().tolist() + output_token_ids = output_token_ids[input_length:] + for each_eos_token_id in eos_token_id: + if output_token_ids[-1] == each_eos_token_id: + output_token_ids = output_token_ids[:-1] + response = tokenizer.decode(output_token_ids) + + yield response + # stop when each sentence is finished, or if we exceed the maximum length + if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores): + break + + +def on_btn_click(): + del st.session_state.messages + + +@st.cache_resource +def load_model(): + model = ( + AutoModelForCausalLM.from_pretrained(model_dir, trust_remote_code=True) + .to(torch.bfloat16) + .cuda() + ) + tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True) + return model, tokenizer + + +def prepare_generation_config(): + with st.sidebar: + max_length = st.slider("Max Length", min_value=8, max_value=32768, value=32768) + top_p = st.slider("Top P", 0.0, 1.0, 0.8, step=0.01) + temperature = st.slider("Temperature", 0.0, 1.0, 0.1, step=0.01) + st.button("Clear Chat History", on_click=on_btn_click) + + generation_config = GenerationConfig(max_length=max_length, top_p=top_p, temperature=temperature) + + return generation_config + + +user_prompt = "<|im_start|>user\n{user}<|im_end|>\n" +robot_prompt = "<|im_start|>assistant\n{robot}<|im_end|>\n" +cur_query_prompt = "<|im_start|>user\n{user}<|im_end|>\n<|im_start|>assistant\n" + + +def combine_history(prompt): + messages = st.session_state.messages + meta_instruction = ( + """下面进行角色扮演, + 你扮演一个在外地工作的单身年轻人,收入低下,现在在亲戚家拜年。 + 我将扮演一个刁钻的亲戚,会问询关于学习、婚姻、恋爱等等,我喜欢将观点强行输出给你。 + 请你根据我的询问进行合理的回答。""" + ) + total_prompt = f"<|im_start|>system\n{meta_instruction}<|im_end|>\n" + for message in messages: + cur_content = message["content"] + if message["role"] == "user": + cur_prompt = user_prompt.format(user=cur_content) + elif message["role"] == "robot": + cur_prompt = robot_prompt.format(robot=cur_content) + else: + raise RuntimeError + total_prompt += cur_prompt + total_prompt = total_prompt + cur_query_prompt.format(user=prompt) + return total_prompt + + +def main(): + # torch.cuda.empty_cache() + print("load model begin.") + model, tokenizer = load_model() + print("load model end.") + + user_avator = "assets/user.png" + robot_avator = "assets/robot.jpeg" + logo_avator = "assets/logo.jpeg" + + col1, col2, col3 = st.columns([1,1,1]) + + with col1: + st.write("") + + with col2: + st.image(logo_avator, caption='SpringFestQA') + + with col3: + st.write("") + generation_config = prepare_generation_config() + + # Initialize chat history + if "messages" not in st.session_state: + st.session_state.messages = [] + + # Display chat messages from history on app rerun + for message in st.session_state.messages: + with st.chat_message(message["role"], avatar=message.get("avatar")): + st.markdown(message["content"]) + + # Accept user input + if prompt := st.chat_input("What is up?"): + # Display user message in chat message container + with st.chat_message("user", avatar=user_avator): + st.markdown(prompt) + real_prompt = combine_history(prompt) + # Add user message to chat history + st.session_state.messages.append({"role": "user", "content": prompt, "avatar": user_avator}) + + with st.chat_message("robot", avatar=robot_avator): + message_placeholder = st.empty() + for cur_response in generate_interactive( + model=model, + tokenizer=tokenizer, + prompt=real_prompt, + additional_eos_token_id=92542, + **asdict(generation_config), + ): + # Display robot response in chat message contain + message_placeholder.markdown(cur_response + "▌") + cur_response = cur_response.strip('<|im_end|>') + message_placeholder.markdown(cur_response) # pylint: disable=undefined-loop-variable + # Add robot response to chat history + st.session_state.messages.append( + { + "role": "robot", + "content": cur_response, # pylint: disable=undefined-loop-variable + "avatar": robot_avator, + } + ) + torch.cuda.empty_cache() + + +if __name__ == "__main__": + main()