Spaces:
Sleeping
Sleeping
File size: 9,225 Bytes
fcc7d9c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
import gradio as gr
import json
import torch
from transformers import pipeline, AutoModelForSequenceClassification, AutoTokenizer
import os
import sys
import traceback
# --- 1. モデルとパイプラインの初期化 ---
# NOTE: モデルの初期化はアプリケーション起動時に一度だけ実行し、グローバル変数に格納します。
# モデル初期化のヘルパー関数
def safe_initialize_pipeline(task, model_name, fallback_model_name=None, is_zero_shot=False):
"""Hugging Faceパイプラインを安全に初期化する関数"""
try:
if is_zero_shot:
# Zero-Shot Classification専用の初期化
classifier = pipeline(
task,
model=model_name,
tokenizer=model_name,
device=0 if torch.cuda.is_available() else -1
)
else:
# 汎用パイプラインの初期化
classifier = pipeline(
task,
model=model_name,
tokenizer=model_name,
)
print(f"✅ {task} パイプラインをモデル: {model_name} でロードしました。")
return classifier
except Exception as e:
print(f"❌ {task} の初期化に失敗 (モデル: {model_name})。代替モデルを試行します。")
# エラー発生時の代替処理
if fallback_model_name:
try:
# 汎用分類モデルをロード
model = AutoModelForSequenceClassification.from_pretrained(fallback_model_name)
tokenizer = AutoTokenizer.from_pretrained(fallback_model_name)
# 分類タスクとして再定義
classifier = pipeline(
"text-classification",
model=model,
tokenizer=tokenizer,
id2label={0: "Negative", 1: "Positive"}
)
print(f"✅ {task} パイプラインを代替モデル: {fallback_model_name} でロードしました。")
return classifier
except Exception as fallback_e:
print(f"致命的なエラー: 代替モデルのロードにも失敗しました。")
print(f"詳細: {fallback_e}")
sys.exit(1)
else:
print(f"致命的なエラー: フォールバックモデルが定義されていません。")
sys.exit(1)
# モデル定義
TONE_MODEL_NAME = "cl-tohoku/bert-base-japanese-whole-word-masking"
STAR_MODEL_NAME = "izumi-lab/bert-base-japanese-v2"
FALLBACK_MODEL_NAME = TONE_MODEL_NAME
# モデルの初期化 (アプリケーション開始時に一度だけ実行)
print("--- モデル初期化開始 ---")
TONE_CLASSIFIER = safe_initialize_pipeline("sentiment-analysis", TONE_MODEL_NAME, FALLBACK_MODEL_NAME)
STAR_CLASSIFIER = safe_initialize_pipeline("zero-shot-classification", STAR_MODEL_NAME, FALLBACK_MODEL_NAME, is_zero_shot=True)
print("--- モデル初期化完了 ---")
# --- 2. 分析ロジック関数 ---
def analyze_tone(text, classifier):
"""テキストの感情・トーンを分析し、熱意スコアを算出する。"""
try:
results = classifier(text)
result = results[0]
# 'Positive' のスコアを抽出 (モデルの出力に依存)
sentiment_score = result.get('score', 0.5)
enthusiasm_score = round(sentiment_score * 100, 2)
return {
"enthusiasm_score": enthusiasm_score
}
except Exception as e:
print(f"感情・トーン分析中にエラーが発生しました: {e}")
return {"enthusiasm_score": 50.0} # エラー時は中間スコアを返す
def check_star_suitability(self_pr_text, classifier):
"""STAR法適合度をチェックし、不足要素を特定する。"""
try:
candidate_labels = ["状況 (Situation)", "課題 (Task)", "行動 (Action)", "結果 (Result)"]
star_results = classifier(
self_pr_text,
candidate_labels,
multi_label=True
)
# スコアとラベルを辞書にまとめる
star_scores = {label: round(score * 100, 2) for label, score in zip(star_results['labels'], star_results['scores'])}
# 適合度と不足要素の判定ロジック
insufficiency_threshold = 70.0
missing_elements = []
total_score = 0
for label, score in star_scores.items():
total_score += score
if score < insufficiency_threshold:
missing_elements.append(label)
# 総合適合度スコア (4要素の平均スコア)
suitability_score = round(total_score / len(candidate_labels), 2)
# JSON出力データ
analysis_reason = {
"適合度スコア": suitability_score,
"論理的": suitability_score >= 75.0,
"不足要素": missing_elements,
"詳細スコア": star_scores
}
return {
"suitability_score": suitability_score,
"analysis_json": analysis_reason
}
except Exception as e:
print(f"STAR法適合度チェック中にエラーが発生しました: {e}")
print(traceback.format_exc())
return {
"suitability_score": 50.0,
"analysis_json": {
"適合度スコア": 50.0,
"論理的": False,
"不足要素": ["エラーのため評価不可"],
"詳細スコア": {"Error": 50.0}
}
}
# --- 3. Gradioインターフェース用メイン処理 ---
def run_es_analysis(es_text, self_pr_text):
"""
Gradioの入力を受け取り、AI分析を実行して結果を返す関数。
"""
if not es_text or not self_pr_text:
return "入力テキストが不足しています。", 0.0, 0.0, json.dumps({"Error": "入力データ不足"})
# 1. 感情・トーン分析
tone_analysis = analyze_tone(es_text, TONE_CLASSIFIER)
enthusiasm_score = tone_analysis['enthusiasm_score']
# 2. STAR法適合度チェック
star_analysis = check_star_suitability(self_pr_text, STAR_CLASSIFIER)
star_score = star_analysis['suitability_score']
analysis_json_data = star_analysis['analysis_json']
# 3. 最終スコアの統合
# 感情スコア 50%, STARスコア 50% の重み付け
final_potential_score = (enthusiasm_score * 0.5) + (star_score * 0.5)
# 結果の整形
final_score_str = f"統合スコア: {round(final_potential_score, 2)} / 100"
# JSON出力を整形して文字列化
json_output = json.dumps(analysis_json_data, indent=4, ensure_ascii=False)
return final_score_str, enthusiasm_score, star_score, json_output
# --- 4. Gradioインターフェースの定義 ---
# テストデータ
DEFAULT_ES_TEXT = """
貴社が推進する「グローバル×ローカル」のデジタル変革戦略に深く共感し、志望いたしました。私は大学時代、学園祭の実行委員長として、従来の集客方法に課題を感じ、SNSを活用した新しいプロモーション戦略を立案・実行しました。これにより、来場者数を前年比150%に増加させるという明確な結果を出しました。この経験から学んだ、現状を打破するための課題設定力と、関係者を巻き込む実行力を貴社で活かし、世界中の顧客に感動を届けるシステムを構築したいと強く願っています。
"""
DEFAULT_SELF_PR = """
私は大学時代、学園祭の実行委員長として、従来の集客方法に課題を感じ、SNSを活用した新しいプロモーション戦略を立案・実行しました。これにより、来場者数を前年比150%に増加させるという明確な結果を出しました。課題はSNSチームのメンバーのモチベーション管理でしたが、個別の面談を通じて主体的な関与を引き出し、最終的に全員が目標達成に貢献しました。
"""
# Gradioインターフェースの構築
iface = gr.Interface(
fn=run_es_analysis,
title="新卒ES AI評価デモンストレーション",
description="エントリーシートの内容を、感情・トーン、およびSTAR法に基づいた論理構造の観点から自動評価します。",
inputs=[
gr.Textbox(label="ES全体テキスト (志望動機など)", lines=10, value=DEFAULT_ES_TEXT),
gr.Textbox(label="自己PRセクション (STAR法チェック対象)", lines=8, value=DEFAULT_SELF_PR)
],
outputs=[
gr.Textbox(label="🌟 最終ポテンシャル評価スコア (統合)", type="text"),
gr.Number(label="熱意・トーン分析スコア (50%)", precision=2),
gr.Number(label="STAR法 総合適合度スコア (50%)", precision=2),
gr.JSON(label="論理構造の詳細分析 (JSON出力)")
],
allow_flagging="never",
theme="soft"
)
# if __name__ == "__main__":
# # Gradioアプリの実行 (ローカル実行用)
# iface.launch() |