oyasai commited on
Commit
a01ff80
·
verified ·
1 Parent(s): 27d9468

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +270 -0
app.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import joblib
3
+ import numpy as np
4
+ import pandas as pd
5
+ import os
6
+ import re
7
+ import json
8
+ from sklearn.feature_extraction.text import TfidfVectorizer
9
+ from sklearn.ensemble import RandomForestClassifier
10
+ from sklearn.pipeline import Pipeline
11
+ import matplotlib.pyplot as plt
12
+
13
+ # モデルとベクトル化器のパス
14
+ MODEL_PATH = os.path.join(os.path.dirname(__file__), "models", "email_classifier.pkl")
15
+
16
+ # モデルの読み込み関数
17
+ def load_model():
18
+ try:
19
+ # 学習済みモデルが存在する場合はロード
20
+ if os.path.exists(MODEL_PATH):
21
+ model = joblib.load(MODEL_PATH)
22
+ print("事前学習済みモデルを読み込みました")
23
+ return model
24
+ else:
25
+ # モデルが存在しない場合は簡易版を作成
26
+ print("モデルが見つからないため、簡易版を作成します")
27
+ return create_simple_model()
28
+ except Exception as e:
29
+ print(f"モデル読み込みエラー: {e}")
30
+ return create_simple_model()
31
+
32
+ # 簡易モデルの作成関数
33
+ def create_simple_model():
34
+ # サンプルデータ
35
+ emails = [
36
+ "研究プロジェクトについての問い合わせです。詳細資料をご提供いただけますか?",
37
+ "共同研究の可能性について相談したいです。来週お時間ありますか?",
38
+ "I am interested in your research project. Could you provide more details?",
39
+ "I would like to discuss potential collaboration. Are you available next week?",
40
+ "特別価格でのご提供!今だけの限定キャンペーン!お早めに!",
41
+ "緊急!あなただけの特別オファーです!今すぐクリック!",
42
+ "SPECIAL OFFER! Limited time discount! Click now!",
43
+ "URGENT! Exclusive deal just for you! Don't miss out!"
44
+ ]
45
+
46
+ # 0: 正当な問い合わせ、1: 営業・スパム
47
+ labels = [0, 0, 0, 0, 1, 1, 1, 1]
48
+
49
+ # 簡易モデルの作成
50
+ model = Pipeline([
51
+ ('vectorizer', TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),
52
+ ('classifier', RandomForestClassifier(n_estimators=50, random_state=42))
53
+ ])
54
+
55
+ # 学習
56
+ model.fit(emails, labels)
57
+
58
+ # モデルディレクトリがなければ作成
59
+ os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True)
60
+
61
+ # モデルを保存
62
+ joblib.dump(model, MODEL_PATH)
63
+
64
+ return model
65
+
66
+ # モデルのロード
67
+ model = load_model()
68
+
69
+ # テキスト前処理関数
70
+ def preprocess_text(text):
71
+ """テキストの簡易前処理"""
72
+ if not isinstance(text, str):
73
+ return ""
74
+
75
+ # 小文字化
76
+ text = text.lower()
77
+
78
+ # 余分な空白の削除
79
+ text = re.sub(r'\s+', ' ', text).strip()
80
+
81
+ return text
82
+
83
+ # 予測関数
84
+ def predict_email_type(email_text):
85
+ """メールの種類を予測"""
86
+ # 前処理
87
+ processed_text = preprocess_text(email_text)
88
+
89
+ if not processed_text:
90
+ return {
91
+ "prediction": "入力が空です",
92
+ "legitimate_prob": 0,
93
+ "spam_prob": 0,
94
+ "confidence": 0,
95
+ "features": []
96
+ }
97
+
98
+ # 予測
99
+ prediction = model.predict([processed_text])[0]
100
+ probabilities = model.predict_proba([processed_text])[0]
101
+
102
+ # 結果の整形
103
+ result = {
104
+ "prediction": "正当な問い合わせ" if prediction == 0 else "営業・スパム",
105
+ "legitimate_prob": float(probabilities[0]),
106
+ "spam_prob": float(probabilities[1]),
107
+ "confidence": float(probabilities[prediction]),
108
+ }
109
+
110
+ # 特徴量の分析(可能な場合)
111
+ try:
112
+ vectorizer = model.named_steps['vectorizer']
113
+ feature_names = vectorizer.get_feature_names_out()
114
+
115
+ # 文書ベクトルの取得
116
+ transformed = vectorizer.transform([processed_text])
117
+
118
+ # 非ゼロの特徴量を取得
119
+ nonzero_indices = transformed.nonzero()[1]
120
+
121
+ # 特徴量と重みのペアを作成
122
+ features = []
123
+ for idx in nonzero_indices:
124
+ if idx < len(feature_names):
125
+ features.append((feature_names[idx], transformed[0, idx]))
126
+
127
+ # 重みで降順ソート
128
+ features.sort(key=lambda x: x[1], reverse=True)
129
+
130
+ # 上位10個の特徴量を返す
131
+ result["features"] = [(str(f), float(w)) for f, w in features[:10]]
132
+
133
+ except Exception as e:
134
+ result["features"] = []
135
+ print(f"特徴量分析エラー: {e}")
136
+
137
+ return result
138
+
139
+ # 予測結果の可視化
140
+ def create_probability_chart(legitimate_prob, spam_prob):
141
+ """確率のバーチャートを作成"""
142
+ categories = ['正当な問い合わせ', '営業・スパム']
143
+ values = [legitimate_prob, spam_prob]
144
+
145
+ plt.figure(figsize=(8, 4))
146
+ bars = plt.bar(categories, values, color=['green', 'red'])
147
+
148
+ # バーの上��値を表示
149
+ for bar, val in zip(bars, values):
150
+ plt.text(bar.get_x() + bar.get_width()/2, val, f'{val:.2%}',
151
+ ha='center', va='bottom')
152
+
153
+ plt.ylim(0, 1.0)
154
+ plt.ylabel('確率')
155
+ plt.title('メール種類の予測確率')
156
+ plt.tight_layout()
157
+
158
+ return plt
159
+
160
+ # サンプルメールの読み込み
161
+ def load_sample_emails():
162
+ """サンプルメールのロード"""
163
+ samples = {
164
+ "legitimate": [
165
+ "研究プロジェクトについて問い合わせします。貴研究所の量子コンピューティングに関する最新の成果に興味があり、詳細資料をいただけないでしょうか。よろしくお願いいたします。",
166
+ "I am writing to inquire about your research on quantum computing. I am particularly interested in your recent findings and would appreciate any additional materials you could provide. Thank you for your consideration."
167
+ ],
168
+ "spam": [
169
+ "【緊急】特別キャンペーン実施中!今だけ50%OFF!このチャンスをお見逃しなく!今すぐクリックして特典をゲット!期間限定なのでお早めに!",
170
+ "URGENT! SPECIAL OFFER! 50% OFF TODAY ONLY! Don't miss this amazing opportunity! Click now to claim your exclusive bonus! Limited time offer!"
171
+ ]
172
+ }
173
+ return samples
174
+
175
+ # Gradioインターフェースの定義
176
+ def create_interface():
177
+ # サンプルメールのロード
178
+ samples = load_sample_emails()
179
+
180
+ with gr.Blocks(title="メール判別システム") as demo:
181
+ gr.Markdown("# 研究関連メールとスパム/営業メールの判別システム")
182
+ gr.Markdown("テキストボックスにメール内容を入力して「分析」ボタンをクリックしてください。システムがそのメールが正当な研究問い合わせか、営業・スパムメールかを判定します。")
183
+
184
+ with gr.Row():
185
+ with gr.Column(scale=2):
186
+ # 入力エリア
187
+ email_input = gr.Textbox(
188
+ label="メール内容",
189
+ placeholder="ここにメール本文を入力してください...",
190
+ lines=10
191
+ )
192
+
193
+ # サンプルボタン
194
+ with gr.Row():
195
+ legitimate_btn = gr.Button("研究問い合わせサンプル")
196
+ spam_btn = gr.Button("営業・スパムサンプル")
197
+
198
+ analyze_btn = gr.Button("分析", variant="primary")
199
+
200
+ with gr.Column(scale=1):
201
+ # 結果表示エリア
202
+ result_label = gr.Label(label="判定結果")
203
+ prob_chart = gr.Plot(label="確率分布")
204
+
205
+ # 特徴語の表示
206
+ features_md = gr.Markdown(label="重要な特徴語")
207
+
208
+ # サンプルボタンの機能
209
+ legitimate_btn.click(
210
+ lambda: np.random.choice(samples["legitimate"]),
211
+ outputs=email_input
212
+ )
213
+
214
+ spam_btn.click(
215
+ lambda: np.random.choice(samples["spam"]),
216
+ outputs=email_input
217
+ )
218
+
219
+ # 分析ボタンの機能
220
+ def process_email(text):
221
+ result = predict_email_type(text)
222
+
223
+ # ラベル用のデータ
224
+ label_data = {
225
+ "正当な問い合わせ": result["legitimate_prob"],
226
+ "営業・スパム": result["spam_prob"]
227
+ }
228
+
229
+ # 特徴語のマークダウン
230
+ features_text = "### 検出された重要な特徴語\n"
231
+ if result["features"]:
232
+ for feature, weight in result["features"]:
233
+ features_text += f"- {feature}: {weight:.4f}\n"
234
+ else:
235
+ features_text += "特徴語の分析に失敗しました。"
236
+
237
+ # 確率チャート
238
+ chart = create_probability_chart(
239
+ result["legitimate_prob"],
240
+ result["spam_prob"]
241
+ )
242
+
243
+ return label_data, chart, features_text
244
+
245
+ analyze_btn.click(
246
+ process_email,
247
+ inputs=email_input,
248
+ outputs=[result_label, prob_chart, features_md]
249
+ )
250
+
251
+ gr.Markdown("""
252
+ ## 使い方
253
+ 1. テキストボックスにメール内容を入力する(または「サンプル」ボタンをクリック)
254
+ 2. 「分析」ボタンをクリックして結果を確認
255
+
256
+ ## システムについて
257
+ このシステムは機械学習を使って、研究に関する正当な問い合わせと営業・スパムメールを自動的に判別します。
258
+ 判別結果は確率で表示され、どの単語や表現がその判断に寄与したかも表示されます。
259
+
260
+ 注意: このデモは学習データが限られているため、精度は実用レベルではありません。実際の運用では、より多くの学習データでモデルを調整する必要があります。
261
+ """)
262
+
263
+ return demo
264
+
265
+ # インターフェースの作成と起動
266
+ demo = create_interface()
267
+
268
+ # Hugging Face Spacesでの実行
269
+ if __name__ == "__main__":
270
+ demo.launch()