LE-NER / app.py
jslin09's picture
Update app.py
018d52d verified
import spaces
import gradio as gr
import requests
import json
import time
import re
import os
import torch
from transformers import pipeline
pipe = pipeline("text-generation", model="jslin09/gemma2-2b-ner", device="cuda")
all_elements = {'LEO_SOC': ('犯罪主體', 'Subject of Crime'),
'LEO_VIC': ('客體', 'Victim'),
'LEO_ACT': ('不法行為', 'Behavior'),
'LEO_SLE': ('主觀要件', 'Subjective Legal Element of the Offense'),
'LEO_CAU': ('因果關係', 'Causation'),
'LEO_ROH': ('危害結果', 'Result of Hazard'),
'LEO_ATP': ('未遂', 'Attempted'),
'LEO_ACC': ('既遂', 'Accomplished'),
'LEO_ABA': ('中止', 'Abandonment'),
'LEO_PRP': ('預備', 'Preparation'),
'ILG_GLJ': ('阻卻違法事由', 'Ground of Legal Justification'),
'ILG_SDE': ('正當防衛', 'Self-Defense'),
'ILG_NEC': ('緊急避難', 'Emergency Avoidance'),
'CUL_INS': ('心神喪失', 'Insane'),
'CUL_FBD': ('精神耗弱', 'Feebleminded'),
'CUL_ALC': ('原因自由行為', 'Actio Libera in Causa'),
'CUL_RPS': ('責任能力', 'Responsibility'),
'CUL_ANP': ('期待可能性', 'Anticipated Possibility'),
'CUL_GUM': ('犯罪意識', 'Guilty Mind')
}
#secret = os.environ.get("HF_TOKEN")
#llm_server = os.environ.get("REMOTE_LLM_SERVER")
legal_element_tags = ['LEO_SOC', 'LEO_VIC', 'LEO_ACT', 'LEO_SLE', 'LEO_CAU', 'LEO_ROH']
def get_prompt(content, tag, tag_name):
'''
說明
get_prompt(): 組合成經過指令微調後的模型所能識別的格式的提示詞樣板。
Parameters:
content (str): 所要送去識別的短文。
tag (str): 構成要件標籤文字,為英文縮寫組成的頭文字字串。
tag_name (str): 構成要件要素中文名稱。
Return:
task_template (str): 組合好的提示詞樣板。
'''
paragraph = content
task = f"請標示出以下段落中的{tag_name}構成要件要素,對應的標籤是[{tag}]。\n\n"
task_template = f'{task}#####\n{paragraph}\n\n#####\n構成要件要素:\n'
# tag_result = f'{task_template}\n#####\n標註結果: \n'
return task_template
def tag_in_parts(tag_content, tag):
'''
說明:
將具有標註結果的文章,找出標籤及名稱,並切成 tuple。
Parameters:
tag_content (str): 已經標註完畢並有標籤的內容。
tag (str): 標籤名稱,英文,沒有括號。
Return:
parts ([tuple]): 識別出構成要件子句及其對應的標籤中文名稱的 tuple 串列。
'''
# 使用正規表示式找出所有構成要件要素文字的起始位置
# print(f'尋找的目標字串: [{tag}]')
# 加入 re.escape() 是為了避免處理到有逸脱字元的字串會報錯而中斷程式執行
# print(f"(tag_in_parts) 構成要件要素:\n{tag}")
start_index = 0
# 使用正規表示式找出所有構成要件要素文字的起始位置
findall_tag_start = [m.start() for m in re.finditer(re.escape(f"[{tag}]"), tag_content)] # 起始標籤開始位置
findall_tag_end = [m.start() for m in re.finditer(re.escape(f"[/{tag}]"), tag_content)] # 結束標籤開始位置
# print(f"(tag_in_parts) 有標籤的「犯罪事實」:\n{tag_content}")
# print(f'標籤名稱開始位置:{findall_tag_start}, 結束位置:{findall_tag_end}')
try:
parts = [(f"{tag_content[start_index:findall_tag_start[0]]}", None)] # 第一個構成要件要素文字之前的句子
except IndexError:
parts = []
# 找出每個構成要件要素子句所在位置,取出子句文字並加以標注構成要件名稱。
# 回傳的標色字串串列,要標色的應該是一組 tuple: ('要標注的字串', '構成要件名稱')
# 不標色的,就單純字串。
# 然後組成一個串列回傳。
for j, clause_idx in enumerate(findall_tag_start):
tag_text = tag_content[clause_idx + len(tag) + 2:findall_tag_end[j]] # 拆出標籤括號中的文字
# print(f"{tag_text}, 開始位址:{clause_idx}, 結束位址:{findall_tag_end[j]}")
parts.append((f"{tag_text}", f"{all_elements[tag][0]}")) # 構成要件子句 tuple: ('構成要件子句', tag_name)
closed_tag = findall_tag_end[j] + len(tag) + 3
try:
next_open_tag = findall_tag_start[j+1]
parts.append((f"{tag_content[closed_tag: next_open_tag]}", None)) # 結束標籤之後到下一個標籤前的文字
except IndexError:
parts.append((f"{tag_content[findall_tag_end[-1] + len(tag) + 3:]}", None)) # 加入最後一句
# print(f"識別後嵌入的結果:\n{parts}\n")
return parts
def ner_extract(text, le_list):
'''
說明:
依照傳入的構成要件要素 tuple 來拆解原始犯罪事實中的構成要件,並傳回構成要件要素 dict 串列。
Parameters:
text (str): 無標籤的「犯罪事實」
le_list ([tuple]): 構成要件要素子句與對應標籤的 tuple 串列。 Tuple 為:('構成要件要素子句', '構成要件要素名稱')
Return:
ner_list ([dict]): 識別出的實體文字、標籤名稱、在文章字串中的開始及結束位置。
ner_dict = {'entity': (str), # 構成要件要素名稱
'word': (str), # 構成要件要素子句
'index': (int), # 索引值,在回圈中累進。
'start': (int), # 構成要件要素子句開始位址
'end': (int) # 構成要件要素子句結束位址
}
'''
ner_list = []
index = 0
for idx in range(len(le_list)):
# print(f"(ner_extract) 構成要件要素 tuple:\n{le_list[idx]}")
# start_index = 0
# 使用正規表示式找出所有構成要件要素文字的起始位置
findall_clause_start = [m.start() for m in re.finditer(re.escape(f"{le_list[idx][0]}"), text)]
findall_clause_end = [m.end() for m in re.finditer(re.escape(f"{le_list[idx][0]}"), text)]
# print(f"犯罪事實:\n{text}")
# print(f'(ner_extract) 構成要件:{le_list[idx][0]}, 開始位置:{findall_clause_start}, 結束位置:{findall_clause_end}')
ner_dict = {}
for i, indx in enumerate(findall_clause_start):
ner_dict = {'entity': '犯罪主體', # 字典範本,要放在回圈中,以便每一圈都會更新。
'word': '甲圈圈',
'index': index,
'start': 0,
'end': 0}
ner_dict['entity'] = le_list[idx][1]
ner_dict['word'] = le_list[idx][0]
ner_dict['index'] = index
ner_dict['start'] = findall_clause_start[i]
ner_dict['end'] = findall_clause_start[i] + len(le_list[idx][0])
ner_list.append(ner_dict)
index = index + 1
return ner_list
@spaces.GPU(duration=180)
def le_ner(content, legal_element_tags=legal_element_tags):
'''
說明:
Legal Elements NER
依據給定的構成要件標籤,識別出短文中的構成要件要素子句。
Parameters:
content (str): 要識別出構成要件要素的無標籤短文字串。
legal_element_tags (list): 標籤英文字串,不含方括號。
Return:
tag_result (list[tuple]): 識別結果的 tuple 串列。每個 tuple 元素為 ('識別出的子句', '構成要件tag')。
'''
temples = [] # 存放每個已經指定標籤的提示詞字串樣板
le_list = [] # 識別完成的構成要件要素 tuple 串列, ('構成要件子句','構成要件要素中文名稱')
# 建立每一個構成要件的提示詞樣板
for idx in range(len(legal_element_tags)):
temples.append(get_prompt(content, legal_element_tags[idx], all_elements[legal_element_tags[idx]][0]))
# 逐一識別出每個要件要素
le_set = set()
loop_time = time.time() # 開始進入迴圈的時間
# 逐一識別出每個要件要素
for idx in range(len(legal_element_tags)):
prompt = temples[idx]
# print(f"識別「{all_elements[legal_element_tags[idx]][0]}」\n")
loop_time = time.time()
pipe_result = pipe(prompt, max_new_tokens=1200)# 使用 Transformers 中的 pipeline 來掛入 NER Model
# print(f"Pipe的回應內容:\n{pipe_result[0]['generated_text'].split('標註結果:')[1]}")
actual_response = pipe_result[0]['generated_text']
response_head = actual_response.split("標註結果:\n")[0] # 「標註結果:」前的內容
try:
response_body = actual_response.split("標註結果:\n")[1] # 「標註結果:」的內容
except IndexError:
response_body = 'None'
# print(f"分割後的內容:\n{response_body}")
# tag_result = tag_in_color(actual_response, legal_element_tags[idx]) # 將回應內容進行標籤標色
tag_result = tag_in_parts(response_body, legal_element_tags[idx]) # 將回應內容進行具名實體標籤拆解
for i, element_tuple in enumerate(tag_result):
if element_tuple[1] != None: # 如果有識別出標籤
print(element_tuple)
le_set.add(element_tuple)
else:
pass
le_list = list(le_set) # 集合型別轉 list
print(f"--- 全部執行耗時:{(time.time() - loop_time)}秒 ---\n")
ner_result = ner_extract(content, le_list)
ner_result_dict = {"text": content, "entities": ner_result}
# print(f'組合完成的 NER 字典:\n{ner_result_dict}')
return ner_result_dict
examples = ["林珈羽能預見任意將所有之金融機構帳戶資料交付予他人,足供他人用為詐欺等犯罪後收受匯款,以遂其掩飾或隱匿犯罪所得財物目的之工具,詎以前開結果之發生亦不違其本意,竟基於幫助他人從事不法行為之犯意,於民國100年4月21日前之不詳時間,在不詳地點,將其向苗栗市農會所申請之帳號00000000000000號帳戶之存摺、提款卡(包括密碼),以不詳之代價,提供與不詳年籍之人使用。而該不詳年籍人士與詐騙集團成員,基於意圖為自己不法之所有,於同年月21日中午12時19分許,撥打電話向被害人張培超以假冒好友謊稱急需用錢等詐騙手法,使張培超誤以為真,而依指示操作後匯出款項新臺幣10萬元至林珈羽上開帳戶內而受騙。",
"錢旺來雖明知不法犯罪集團經常使用人頭帳戶,向被害人施用詐術,藉此逃避檢警人員之追緝,並預見向其取得帳戶之人,會以該帳戶作為詐欺取財之不法所用,竟仍基於幫助詐欺之犯意,於民國106年1月11日某時,將其所申辦之上海商業銀行(下稱上海商銀)帳號00000000000000號帳戶之提款卡、密碼,以宅急便之方式,寄送予某真實姓名年籍不詳之詐欺集團成員,供該詐欺集團成員用於詐欺取財之犯行。嗣該詐欺集團成員即意圖為自己不法之所有,於106年1月13日18時許,撥打電話予洪菁霞,向洪菁霞佯稱係其友人,急需資金周轉,致洪菁霞陷於錯誤,於同日14時35分許,至台中市○區○○路000號之郵局,臨櫃匯款新臺幣(下同)10萬元至錢旺來前揭上海商銀帳戶內,隨即遭提領一空。",
"梅友虔明知金融帳戶之存摺、提款卡及密碼係供自己使用之重要理財工具,關係個人財產、信用之表徵,並可預見一般人取得他人金融帳戶使用常與財產犯罪密切相關,且取得他人帳戶之目的在於掩飾犯罪所得之財物或財產上利益不易遭人追查,對於提供帳戶存摺、提款卡及密碼雖無引發他人萌生犯罪之確信,但仍以縱若有人持以犯罪,亦無違反其本意之幫助詐欺之不確定故意,於民國104年11月12日某時,在桃園市中壢區某便利商店內,將其所申辦之臺灣銀行中壢分行帳號000000000000號帳戶(下稱臺灣銀行帳戶)之存摺、提款卡及密碼,以宅急便方式寄送至高雄市○○區○○○路000號予「黃冠智」之成年人使用,容認該「黃冠智」得以使用作為詐欺取財之工具,並以此方式幫助其從事詐欺取財之犯行。迨「黃冠智」取得上開帳戶存摺、提款卡及密碼後,即基於意圖為自己不法所有之詐欺取財犯意,分別於附表所示時間撥打電話予謝家富、陳品蓁,佯以附表所示情節,致謝家富、陳品蓁均陷於錯誤,而分別於附表所示匯款時間,匯款如附表所示金額至上開帳戶內,並旋遭提領一空。案經謝家富、陳品蓁訴由桃園市政府警察局中壢分局移送臺灣桃園地方法院檢察署檢察官偵查後聲請簡易判決處刑。",
"李大明預見將自己金融機構帳戶存摺、金融卡及密碼提供他人使用,可能幫助他人從事財產犯罪,竟基於幫助他人犯詐欺取財罪之不確定故意,於民國106年7月3日前某日,將其申辦之中華郵政股份有限公司帳號00000000000000號帳戶(下稱本案帳戶)之存摺、金融卡及密碼交予姓名年籍不詳之詐欺集團成員使用,嗣該詐欺集團成員(無證據證明該集團成員達3人以上)取得本案帳戶資料後,即意圖為自己不法之所有,基於詐欺取財之犯意,於106年7月3日下午4時許,撥打電話予陳淑美,佯稱為其友人急需借款云云,致陳淑美陷於錯誤,而依指示於同日下午4時許,在桃園市○○區○○○路0段000號之合作金庫銀行內,臨櫃匯款新臺幣(下同)18萬元至本案帳戶內,旋遭提領一空。",
"林通海雖明知不法犯罪集團經常使用人頭帳戶,向被害人施用詐術,仍基於詐欺取財之犯意,於民國112年4月12日,與該不法犯罪集團成員共同謀議後,決定以電話詐騙方式,向被害人李小明實行詐術。其後,林通海等人依計劃行事,由集團成員甲先以電話假冒某知名電信公司的客服人員,告知被害人李小明其名下的手機帳戶有異常費用需支付,並要求被害人提供銀行帳戶資料以利後續處理。被害人李小明信以為真,遂依指示提供其銀行帳戶及相關資料。",
"公訴意旨略以:吳正義可預見將存摺、金融卡及提款密碼金融帳戶資料提供他人時,有供不法詐騙份子利用,而幫助他人為財產犯罪之虞,竟不違背其本意,基於幫助他人詐欺之犯意,於民國106年11月10日前之某日,將其申設之彰化商業銀行股份有限公司北屯分行帳號00000000000000號帳戶(下稱彰化銀行帳戶)之存摺、金融卡及提款密碼交付予真實姓名、年籍不詳之成年人,該成年人於取得上開帳戶資料後,即與詐欺集團之其他成員,共同基於意圖為自己不法所有之犯意聯絡,於106年11月10日18時20分許,撥打電話予黃怡菁,佯稱其係雄獅旅遊之員工,因黃怡菁購買之旅遊商品人員疏失設定為分期付款,需依指示操作自動櫃員機解除設定云云,致黃怡菁陷於錯誤,而於同日19時3分許,以轉帳匯款方式,將新臺幣(下同)2萬9,987元匯入吳正義上開彰化銀行帳戶內,旋遭提領殆盡。因認被告吳正義涉犯刑法第30條第1項、第339條第1項之幫助詐欺取財罪嫌。"
]
with gr.Blocks(fill_height=True,
theme=gr.themes.Soft(primary_hue=gr.themes.colors.indigo,
secondary_hue=gr.themes.colors.fuchsia)) as demo:
gr.Markdown(
"""
<h1 style="text-align: center;">詐欺罪判決書「犯罪事實」構成要件要素識別器</h1>
""")
with gr.Row() as row:
with gr.Column():
content = gr.Textbox(label="犯罪事實", lines=5, placeholder="輸入「犯罪事實」....,或是選取以下範例。")
gr.Examples(examples, label='Examples', inputs=[content])
legal_elements = gr.CheckboxGroup(choices=[("犯罪主體", "LEO_SOC"), ("主觀要件", "LEO_SLE"), ("不法行為","LEO_ACT"), \
('客體','LEO_VIC'), ("因果關係","LEO_CAU"), ("危害結果","LEO_ROH")], \
value=legal_element_tags, # 預設值為全選 \
label="請勾選構成要件要素", info="構成要件要素")
with gr.Row():
output = gr.HighlightedText(label="標註結果")
with gr.Row():
greet_btn = gr.Button("識別構成要件要素", variant="primary")
greet_btn.click(fn=le_ner, inputs=[content, legal_elements], outputs=output, api_name="le_ner")
demo.launch(share=False)