|
|
import gradio as gr |
|
|
import re |
|
|
import matplotlib.pyplot as plt |
|
|
import numpy as np |
|
|
from transformers import pipeline |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer |
|
|
|
|
|
def translate_ai(cn_text): |
|
|
try: |
|
|
return translator(cn_text)[0]["translation_text"] |
|
|
except: |
|
|
return "Translation model error." |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
POLITENESS_FEATURES = { |
|
|
"modals": ["could", "would", "may", "might"], |
|
|
"softener": ["please", "kindly"], |
|
|
"hedging": ["possibly", "perhaps", "a bit", "a little"], |
|
|
"formal": ["professor", "sir", "madam"], |
|
|
"passive": ["is requested", "may cause", "is appreciated"], |
|
|
"appreciation": ["thank", "appreciate", "grateful"] |
|
|
} |
|
|
|
|
|
def extract_politeness_scores(en_text): |
|
|
text = en_text.lower() |
|
|
scores = {} |
|
|
|
|
|
for feat, markers in POLITENESS_FEATURES.items(): |
|
|
score = sum(text.count(m) for m in markers) |
|
|
scores[feat] = min(score, 5) |
|
|
|
|
|
return scores |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def classify_tira(cn_text, en_text): |
|
|
cn = cn_text.lower() |
|
|
en = en_text.lower() |
|
|
|
|
|
cn_amplify = ["能否", "是否方便", "请您帮忙", "抽空", "劳驾", "麻烦帮我"] |
|
|
cn_attenuate = ["尽快", "必须", "需要您", "立即"] |
|
|
cn_redirect = ["给您添麻烦", "不好意思打扰", "不胜感激"] |
|
|
|
|
|
en_amplify = ["please", "could you", "possibly", "would you"] |
|
|
en_attenuate = ["directly", "must", "asap"] |
|
|
en_redirect = ["inconvenience", "may cause"] |
|
|
|
|
|
if any(p in cn for p in cn_redirect) or any(p in en for p in en_redirect): |
|
|
return "Redirect(转向)" |
|
|
|
|
|
if any(p in cn for p in cn_amplify) or any(p in en for p in en_amplify): |
|
|
return "Amplify(放大)" |
|
|
|
|
|
if any(p in cn for p in cn_attenuate) or any(p in en for p in en_attenuate): |
|
|
return "Attenuate(削弱)" |
|
|
|
|
|
return "Retain(保留)" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def draw_dual_radar(ai_scores, human_scores=None): |
|
|
labels = list(ai_scores.keys()) |
|
|
angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False).tolist() |
|
|
|
|
|
ai_vals = list(ai_scores.values()) + [list(ai_scores.values())[0]] |
|
|
angles_plot = angles + [angles[0]] |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(6,6), subplot_kw=dict(polar=True)) |
|
|
|
|
|
ax.fill(angles_plot, ai_vals, color="#1B3B6F80", alpha=0.4, label="AI Translation") |
|
|
ax.plot(angles_plot, ai_vals, color="#1B3B6F", linewidth=2) |
|
|
|
|
|
if human_scores: |
|
|
hum_vals = list(human_scores.values()) + [list(human_scores.values())[0]] |
|
|
ax.fill(angles_plot, hum_vals, color="#D9534F50", alpha=0.3, label="Human Translation") |
|
|
ax.plot(angles_plot, hum_vals, color="#D9534F", linewidth=2) |
|
|
|
|
|
ax.set_xticks(angles) |
|
|
ax.set_xticklabels([l.capitalize() for l in labels]) |
|
|
ax.set_yticks(range(1, 6)) |
|
|
ax.set_ylim(0, 5) |
|
|
|
|
|
ax.set_title("AI vs Human Politeness Radar", fontsize=15, color="#1B3B6F") |
|
|
ax.legend(loc="upper right", bbox_to_anchor=(1.3, 1.1)) |
|
|
|
|
|
return fig |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_tira_table(ai_tira, human_tira=None): |
|
|
if not human_tira: |
|
|
table = f""" |
|
|
| 比较项 | AI | |
|
|
|--------|------------------| |
|
|
| **TIRA 类型** | {ai_tira} | |
|
|
""" |
|
|
else: |
|
|
table = f""" |
|
|
| 比较项 | AI 译文 | 人工译文 | |
|
|
|--------|--------------|----------------| |
|
|
| **TIRA 类型** | {ai_tira} | {human_tira} | |
|
|
| **偏移方向** | {"↑ 放大 / 更远" if ai_tira=="Amplify(放大)" else ("↓ 更直接" if ai_tira=="Attenuate(削弱)" else ("↔ 基本一致" if ai_tira=="Retain(保留)" else "→ 转向"))} | |
|
|
""" |
|
|
return table |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def full_process(cn_text, human_text): |
|
|
en_ai = translate_ai(cn_text) |
|
|
ai_scores = extract_politeness_scores(en_ai) |
|
|
ai_tira = classify_tira(cn_text, en_ai) |
|
|
|
|
|
human_scores = None |
|
|
human_tira = None |
|
|
if human_text and human_text.strip() != "": |
|
|
human_scores = extract_politeness_scores(human_text) |
|
|
human_tira = classify_tira(cn_text, human_text) |
|
|
|
|
|
table = generate_tira_table(ai_tira, human_tira) |
|
|
fig = draw_dual_radar(ai_scores, human_scores) |
|
|
|
|
|
return en_ai, ai_scores, ai_tira, table, fig |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
css = """ |
|
|
h1 {text-align:center; color:#1B3B6F;} |
|
|
label {font-weight:bold;} |
|
|
""" |
|
|
|
|
|
with gr.Blocks(css=css, title="TIRA AI vs Human Comparison System") as demo: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# **📘 TIRA AI vs Human Translation Comparison System** |
|
|
### *TIRA Table + Dual Radar Chart + Politeness Analysis* |
|
|
--- |
|
|
|
|
|
输入中文→自动生成: |
|
|
- AI 翻译 |
|
|
- AI vs Human 礼貌雷达叠加 |
|
|
- AI vs Human TIRA 类型对照表 |
|
|
|
|
|
非常适合学术展示与论文附录材料。 |
|
|
--- |
|
|
""") |
|
|
|
|
|
cn_input = gr.Textbox(lines=4, label="✉ 输入中文邮件内容") |
|
|
human_input = gr.Textbox(lines=3, label="📝 可选:输入人工英文译文") |
|
|
|
|
|
btn = gr.Button("🔍 运行分析", variant="primary") |
|
|
|
|
|
en_out = gr.Textbox(label="🌐 AI 翻译") |
|
|
ai_scores_out = gr.JSON(label="🔬 AI 礼貌特征") |
|
|
ai_tira_out = gr.Textbox(label="📌 AI 的 TIRA 类型") |
|
|
table_out = gr.Markdown(label="📊 TIRA 对照表") |
|
|
radar_out = gr.Plot(label="📉 AI vs Human 双雷达图叠加") |
|
|
|
|
|
btn.click( |
|
|
fn=full_process, |
|
|
inputs=[cn_input, human_input], |
|
|
outputs=[en_out, ai_scores_out, ai_tira_out, table_out, radar_out] |
|
|
) |
|
|
|
|
|
demo.launch() |
|
|
|