"""
허깅페이스 Space를 위한, 개선된 사고 분석 앱 (분류 결과 순위 강조)
app.py 파일로 저장하세요
"""
import gradio as gr
import torch
import numpy as np
import re
import pandas as pd
import os
import tempfile
from transformers import (
AutoTokenizer,
AutoModelForSequenceClassification,
BartForConditionalGeneration
)
# 모델 정보
MODEL_CONFIG = {
# 분류 모델 (필수)
"classification_repo": "jennalee1385/accident_classification",
# 요약 모델 (선택적 - 사용하지 않을 경우 None으로 설정)
"summarization_repo": "gogamza/kobart-summarization",
# 최대 텍스트 길이
"max_length": 256,
# 요약 최대 길이
"summary_max_length": 128
}
# 모델 캐싱을 위한 변수
model_cache = {
"classification_model": None,
"classification_tokenizer": None,
"summarization_model": None,
"summarization_tokenizer": None,
"label_map": None
}
# 순위 배지 HTML 생성 함수
def create_rank_badge(rank):
"""순위에 따른 배지 HTML을 생성합니다"""
if rank == 1:
return f"🥇 {rank}순위"
elif rank == 2:
return f"🥈 {rank}순위"
elif rank == 3:
return f"🥉 {rank}순위"
else:
return f"{rank}순위"
# 확률 바 HTML 생성 함수
def create_probability_bar(probability):
"""확률에 따른 진행 바 HTML을 생성합니다"""
width = int(probability * 100)
bar_color = "#4CAF50" if width > 80 else "#FFC107" if width > 50 else "#F44336"
return f"""
{probability:.2%}
"""
# 샘플 엑셀 파일 생성
def create_sample_excel():
try:
df = pd.DataFrame({
'사고경위': [
'작업자가 계단을 내려오던 중 발을 헛디뎌 넘어졌다.',
'지게차 운전 중 장애물과 부딪혀 머리를 부딪힘',
'용접 작업 중 불꽃이 튀어 화상을 입음'
]
})
file_path = "./사고분석_양식.xlsx"
df.to_excel(file_path, index=False)
print(f"샘플 파일 생성 완료: {os.path.abspath(file_path)}")
return file_path
except Exception as e:
print(f"샘플 엑셀 파일 생성 오류: {e}")
return None
class AccidentAnalysisModel:
"""사고 경위 분석 모델 클래스"""
def __init__(self, model_repo=None):
self.model_repo = model_repo or MODEL_CONFIG["classification_repo"]
self.summarization_repo = MODEL_CONFIG["summarization_repo"]
self.device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"사용 디바이스: {self.device}")
# 모델 로드
self._load_from_huggingface()
def _load_from_huggingface(self):
"""허깅페이스에서 모델을 로드합니다."""
try:
# 캐싱된 모델이 있으면 재사용
if model_cache["classification_model"] is not None:
self.classification_model = model_cache["classification_model"]
self.classification_tokenizer = model_cache["classification_tokenizer"]
self.label_map = model_cache["label_map"]
self.summarization_model = model_cache["summarization_model"]
self.summarization_tokenizer = model_cache["summarization_tokenizer"]
print("캐시된 모델 로드 완료!")
return
print("모델 로드 시작...")
# 1. 분류 모델 로드
print(f"분류 모델 '{self.model_repo}' 로드 중...")
self.classification_tokenizer = AutoTokenizer.from_pretrained(self.model_repo)
self.classification_model = AutoModelForSequenceClassification.from_pretrained(self.model_repo)
self.classification_model.to(self.device)
# 2. 레이블 맵 로드
try:
import json
import requests
# 레이블 맵 파일 URL
label_map_url = f"https://huggingface.co/{self.model_repo}/raw/main/label_map.json"
response = requests.get(label_map_url)
if response.status_code == 200:
self.label_map = response.json()
else:
print("레이블 맵을 찾을 수 없어 기본값 사용")
# 모델의 id2label에서 레이블 맵 가져오기
self.label_map = {str(k): v for k, v in self.classification_model.config.id2label.items()}
print(f"레이블 맵 로드 완료: {self.label_map}")
except Exception as e:
print(f"레이블 맵 로드 오류: {e}")
self.label_map = {str(i): f"클래스 {i}" for i in range(self.classification_model.config.num_labels)}
# 3. 요약 모델 로드 (있는 경우)
if self.summarization_repo:
try:
print(f"요약 모델 '{self.summarization_repo}' 로드 중...")
self.summarization_tokenizer = AutoTokenizer.from_pretrained(self.summarization_repo)
self.summarization_model = BartForConditionalGeneration.from_pretrained(self.summarization_repo)
self.summarization_model.to(self.device)
print("요약 모델 로드 완료!")
except Exception as e:
print(f"요약 모델 로드 오류: {e}")
self.summarization_model = None
self.summarization_tokenizer = None
else:
self.summarization_model = None
self.summarization_tokenizer = None
# 모델 캐싱
model_cache["classification_model"] = self.classification_model
model_cache["classification_tokenizer"] = self.classification_tokenizer
model_cache["summarization_model"] = self.summarization_model
model_cache["summarization_tokenizer"] = self.summarization_tokenizer
model_cache["label_map"] = self.label_map
print("모델 로드 완료!")
except Exception as e:
print(f"모델 로드 오류: {e}")
raise
def enhanced_generate_summary(self, text):
"""개선된 요약 생성 함수"""
try:
# 입력 텍스트 유효성 검사
if not text or len(text.strip()) < 80: # 너무 짧은 텍스트는 그대로 반환
return text
# 모델이 없으면 원본 텍스트 반환
if self.summarization_model is None or self.summarization_tokenizer is None:
return text
# 전처리(필요없는 내용제거)
text = re.sub(r'\([^)]*\)', '', text) # 소괄호와 내용 제거 (...)
text = re.sub(r'\[[^\]]*\]', '', text) # 대괄호와 내용 제거 [...]
text = re.sub(r'\'[^\']*\'', '', text) # 작은 따옴표와 내용 제거 '...'
text = re.sub(r'\"[^\"]*\"', '', text) # 큰 따옴표와 내용 제거 "..."
text = re.sub(r'\s+', ' ', text).strip() # 중복 공백 정리
# 토큰화 및 요약 생성
inputs = self.summarization_tokenizer(
text,
return_tensors="pt",
max_length=512, # 입력 길이 증가
truncation=True,
padding=True
).to(self.device)
# 요약 생성 설정 강화
self.summarization_model.eval()
with torch.no_grad():
summary_ids = self.summarization_model.generate(
inputs["input_ids"],
max_length=MODEL_CONFIG["summary_max_length"],
min_length=30,
num_beams=5, # 빔 개수
early_stopping=True,
length_penalty=1.5,
repetition_penalty=2.5,
no_repeat_ngram_size=3
)
# 디코딩
summary = self.summarization_tokenizer.decode(summary_ids[0], skip_special_tokens=True)
return summary.strip()
except Exception as e:
print(f"요약 생성 오류: {e}")
return text # 오류 발생 시 원본 텍스트 반환
def analyze(self, text, top_k=3):
"""사고 경위를 분석하여 요약과 분류 결과를 반환합니다."""
# 텍스트 유효성 검사
if not text or len(text.strip()) == 0:
return {
"summary": "입력된 텍스트가 없습니다.",
"classification": [],
"error": "텍스트를 입력해주세요."
}
try:
# 텍스트 전처리
text = re.sub(r'\s+', ' ', text) # 여러 공백을 하나로
text = text.strip()
# 1. 요약 생성
summary = self.enhanced_generate_summary(text)
# 분류에 사용할 텍스트 선택 (요약 또는 원문)
# 요약 모델이 있으면 요약을 사용, 없으면 원문 사용
text_for_classification = summary if self.summarization_model else text
# 2. 분류 수행
self.classification_model.eval()
# 토큰화
inputs = self.classification_tokenizer(
text_for_classification,
padding="max_length",
truncation=True,
max_length=MODEL_CONFIG["max_length"],
return_tensors="pt"
).to(self.device)
# 예측
with torch.no_grad():
outputs = self.classification_model(**inputs)
logits = outputs.logits
probabilities = torch.nn.functional.softmax(logits, dim=1)
# 상위 k개 예측 결과
topk_values, topk_indices = torch.topk(probabilities, k=min(top_k, len(self.label_map)), dim=1)
# 결과 포맷팅
classification_results = []
for i in range(min(top_k, len(self.label_map))):
idx = topk_indices[0][i].item()
label_text = self.label_map.get(str(idx), f"클래스 {idx}")
classification_results.append({
"rank": i + 1,
"class": idx,
"class_name": label_text,
"probability": float(topk_values[0][i].item())
})
# 최종 결과 반환
return {
"summary": summary if summary != text else "사고 경위 텍스트가 짧아서 요약을 생략하겠습니다다.",
"classification": classification_results,
"error": None
}
except Exception as e:
print(f"분석 중 오류 발생: {e}")
return {
"summary": "오류 발생",
"classification": [],
"error": str(e)
}
def analyze_batch(self, texts):
"""여러 사고 경위를 배치로 분석합니다."""
results = []
for text in texts:
if not text or len(text.strip()) == 0:
results.append({
"input_text": text,
"summary": "입력된 텍스트가 없습니다.",
"classification": [],
"error": "텍스트를 입력해주세요."
})
continue
try:
# 개별 분석 수행
result = self.analyze(text)
# 입력 텍스트 추가
result["input_text"] = text
results.append(result)
except Exception as e:
results.append({
"input_text": text,
"summary": "오류 발생",
"classification": [],
"error": str(e)
})
return results
def analyze_excel(self, file_path):
"""엑셀 파일의 사고 경위를 분석합니다."""
try:
# 엑셀 파일 읽기
df = pd.read_excel(file_path)
# '사고경위' 열 확인
if '사고경위' not in df.columns:
return None, "엑셀 파일에 '사고경위' 열이 없습니다. 양식을 다운로드하여 사용해주세요."
# 사고 경위 리스트 추출
accident_texts = df['사고경위'].fillna('').tolist()
# 배치 분석 수행
results = self.analyze_batch(accident_texts)
# 결과를 DataFrame으로 변환
result_data = []
for result in results:
# 분류 결과 중 가장 확률이 높은 것 선택
top_classification = result["classification"][0] if result["classification"] else {"class_name": "분류 실패", "probability": 0.0}
# 2, 3위 사고 유형 추가
second_classification = result["classification"][1] if len(result["classification"]) > 1 else {"class_name": "-", "probability": 0.0}
third_classification = result["classification"][2] if len(result["classification"]) > 2 else {"class_name": "-", "probability": 0.0}
result_data.append({
"사고경위": result["input_text"],
"요약": result["summary"],
"1순위 사고유형": top_classification["class_name"],
"1순위 확률": f"{top_classification['probability']:.4f}",
"2순위 사고유형": second_classification["class_name"],
"2순위 확률": f"{second_classification['probability']:.4f}",
"3순위 사고유형": third_classification["class_name"],
"3순위 확률": f"{third_classification['probability']:.4f}"
})
result_df = pd.DataFrame(result_data)
# 임시 파일로 저장
temp_dir = tempfile.gettempdir()
result_file = os.path.join(temp_dir, "사고분석_결과.xlsx")
result_df.to_excel(result_file, index=False)
return result_file, None
except Exception as e:
print(f"엑셀 분석 오류: {e}")
return None, f"엑셀 파일 분석 중 오류 발생: {str(e)}"
# 모델 인스턴스 생성 함수 (캐싱을 위한 팩토리 함수)
def get_model():
"""모델 인스턴스를 반환합니다."""
return AccidentAnalysisModel(model_repo=MODEL_CONFIG["classification_repo"])
# 단일 텍스트 분석 함수
def analyze_single(text):
"""단일 텍스트를 분석합니다."""
model = get_model()
results = model.analyze(text)
# 오류가 있는 경우
if results["error"]:
return results["error"], gr.HTML("분석 오류가 발생했습니다.
")
# 분류 결과를 HTML로 포맷팅
classification_html = ""
for result in results["classification"]:
rank = result["rank"]
class_name = result["class_name"]
probability = result["probability"]
rank_badge = create_rank_badge(rank)
probability_bar = create_probability_bar(probability)
classification_html += f"""
{rank_badge} {class_name}
{probability_bar}
"""
classification_html += "
"
return results["summary"], gr.HTML(classification_html)
# 다수 텍스트 분석 함수
def analyze_multiple(texts):
"""여러 텍스트를 분석합니다."""
model = get_model()
# 텍스트 리스트 생성 (빈 줄 제거)
text_list = [t.strip() for t in texts.split('\n') if t.strip()]
if not text_list:
return "텍스트를 입력해주세요", gr.HTML("분석할 텍스트가 없습니다.
")
# 배치 분석
results = model.analyze_batch(text_list)
# 요약 결과 포맷팅
summary_text = ""
for idx, result in enumerate(results):
summary_text += f"{idx+1}. {result['summary']}\n\n"
# 분류 결과를 HTML로 포맷팅
classification_html = ""
for idx, result in enumerate(results):
short_text = text_list[idx][:50] + "..." if len(text_list[idx]) > 50 else text_list[idx]
classification_html += f"""
{idx+1}
{short_text}
"""
if result["classification"]:
for class_result in result["classification"]:
rank = class_result.get("rank", 1)
class_name = class_result["class_name"]
probability = class_result["probability"]
rank_badge = create_rank_badge(rank)
probability_bar = create_probability_bar(probability)
classification_html += f"""
{rank_badge} {class_name}
{probability_bar}
"""
else:
classification_html += "
분류 결과가 없습니다.
"
classification_html += "
"
classification_html += "
"
return summary_text, gr.HTML(classification_html)
# 엑셀 파일 분석 함수
def analyze_excel_file(file):
"""엑셀 파일을 분석합니다."""
if file is None:
return None, "파일을 업로드해주세요."
model = get_model()
try:
# 업로드된 파일 확인
print(f"업로드된 파일: {file.name}")
# 원본 엑셀 파일 읽기
df = pd.read_excel(file.name)
# '사고경위' 열 확인
if '사고경위' not in df.columns:
return None, "엑셀 파일에 '사고경위' 열이 없습니다. 양식을 다운로드하여 사용해주세요."
# 사고 경위 리스트 추출
accident_texts = df['사고경위'].fillna('').tolist()
# 배치 분석 수행
results = model.analyze_batch(accident_texts)
# 결과 데이터프레임 생성 (원본 데이터프레임 복사)
result_df = df.copy()
# 분석 결과 열 추가
# 기존 열이 없을 경우에만 추가
if '요약' not in result_df.columns:
result_df['요약'] = ''
if '1순위 사고유형' not in result_df.columns:
result_df['1순위 사고유형'] = ''
if '1순위 확률' not in result_df.columns:
result_df['1순위 확률'] = ''
if '2순위 사고유형' not in result_df.columns:
result_df['2순위 사고유형'] = ''
if '2순위 확률' not in result_df.columns:
result_df['2순위 확률'] = ''
if '3순위 사고유형' not in result_df.columns:
result_df['3순위 사고유형'] = ''
if '3순위 확률' not in result_df.columns:
result_df['3순위 확률'] = ''
# 결과 채우기
for i, result in enumerate(results):
if i >= len(result_df): # 안전 검사
break
# 요약 추가
result_df.at[i, '요약'] = result['summary']
# 분류 결과 추가
if result['classification']:
# 1순위
if len(result['classification']) > 0:
top = result['classification'][0]
result_df.at[i, '1순위 사고유형'] = top['class_name']
result_df.at[i, '1순위 확률'] = f"{top['probability']:.4f}"
# 2순위
if len(result['classification']) > 1:
second = result['classification'][1]
result_df.at[i, '2순위 사고유형'] = second['class_name']
result_df.at[i, '2순위 확률'] = f"{second['probability']:.4f}"
# 3순위
if len(result['classification']) > 2:
third = result['classification'][2]
result_df.at[i, '3순위 사고유형'] = third['class_name']
result_df.at[i, '3순위 확률'] = f"{third['probability']:.4f}"
# 결과 파일 저장
result_file = os.path.join(tempfile.gettempdir(), "사고분석_결과.xlsx")
result_df.to_excel(result_file, index=False)
print(f"분석 결과 파일 저장 경로: {result_file}")
return result_file, None
except Exception as e:
print(f"엑셀 파일 분석 중 오류 발생: {e}")
import traceback
traceback.print_exc() # 자세한 오류 정보 출력
return None, f"엑셀 파일 분석 중 오류 발생: {str(e)}"
# 엑셀 양식 다운로드 함수
def download_excel_template():
"""엑셀 양식 파일을 생성하고 경로를 반환합니다."""
return create_sample_excel()
# Gradio 인터페이스 설정
def create_interface():
"""Gradio 인터페이스를 생성합니다."""
with gr.Blocks(title="사고 경위 분석 시스템", css="""
.container { margin: 0 auto; max-width: 1200px; }
.header { text-align: center; margin-bottom: 20px; }
.result-container { border: 1px solid #ddd; border-radius: 10px; padding: 15px; background-color: #f9f9f9; }
.rank-badge { display: inline-block; padding: 3px 8px; border-radius: 12px; font-weight: bold; margin-right: 5px; }
.rank-1 { background-color: #FFD700; color: #000; }
.rank-2 { background-color: #C0C0C0; color: #000; }
.rank-3 { background-color: #CD7F32; color: #000; }
.prob-bar { height: 10px; background-color: #4CAF50; border-radius: 5px; }
.footer { text-align: center; margin-top: 30px; padding-top: 15px; border-top: 1px solid #eee; }
""") as app:
gr.Markdown(
"""
# 🔍 사고 경위 분석 시스템
사고 경위 텍스트를 입력하면 AI가 사고 유형을 분류하고 요약을 제공합니다.
"""
)
with gr.Tabs() as tabs:
# 탭 1: 단일 사고 분석
with gr.TabItem("개별 사고 분석"):
with gr.Row():
with gr.Column():
input_text = gr.Textbox(
label="사고 경위",
placeholder="여기에 사고 경위를 입력하세요...",
lines=10
)
analyze_btn = gr.Button("분석하기", variant="primary")
with gr.Column():
summary_output = gr.Textbox(
label="사고 요약",
lines=5
)
classification_output = gr.HTML(
label="사고 유형 분류 결과",
value="분석 결과가 여기에 표시됩니다.
"
)
analyze_btn.click(
fn=analyze_single,
inputs=[input_text],
outputs=[summary_output, classification_output]
)
gr.Markdown(
"""
### 🔍 개별 사고 분석 사용법
1. 분석할 사고 경위를 텍스트 상자에 입력하세요.
2. '분석하기' 버튼을 클릭하여 결과를 확인하세요.
3. 분석 결과는 사고 요약과 1~3순위 사고 유형 분류를 포함합니다.
"""
)
# 탭 2: 다중 사고 분석
with gr.TabItem("다중 사고 분석"):
gr.Markdown("### 📊 여러 사고를 한 번에 분석")
gr.Markdown("각 사고 경위를 개별 입력 상자에 입력하세요. 분석 결과는 각 입력 아래에 표시됩니다.")
# 분석 함수 수정 - 결과 컴포넌트를 반환하도록
def analyze_multiple_fields(*input_texts):
# 빈 입력은 필터링
valid_inputs = [(i, text) for i, text in enumerate(input_texts) if text and text.strip()]
if not valid_inputs:
return [gr.update(visible=True, value="텍스트를 입력해주세요")] + [gr.update(visible=False) for _ in range(len(input_texts)*2-1)]
model = get_model()
results = []
summary_updates = []
html_updates = []
# 모든 입력 상자에 대한 기본 업데이트 (빈 것은 숨김)
for i in range(len(input_texts)):
if i < len(valid_inputs) and valid_inputs[i][0] == i: # 유효한 입력이 있는 경우
idx, text = valid_inputs[i]
result = model.analyze(text)
results.append(result)
# 요약 업데이트
summary_updates.append(gr.update(visible=True, value=result["summary"]))
# 분류 결과 HTML 생성
classification_html = ""
for class_result in result["classification"]:
rank = class_result.get("rank", 1)
class_name = class_result["class_name"]
probability = class_result["probability"]
rank_badge = create_rank_badge(rank)
probability_bar = create_probability_bar(probability)
classification_html += f"""
{rank_badge} {class_name}
{probability_bar}
"""
classification_html += "
"
html_updates.append(gr.update(visible=True, value=classification_html))
else: # 빈 입력인 경우
summary_updates.append(gr.update(visible=False))
html_updates.append(gr.update(visible=False))
# 결과 반환 (모든 요약과 HTML 컴포넌트에 대한 업데이트)
return summary_updates + html_updates
# 분석 항목 컨테이너 생성
analysis_items = []
summary_outputs = []
classification_outputs = []
# 초기 분석 항목 생성 (3개)
for i in range(3):
with gr.Group():
with gr.Row():
with gr.Column(scale=1):
analysis_items.append(gr.Textbox(
label=f"사고 경위 {i+1}",
placeholder="여기에 사고 경위를 입력하세요...",
lines=3
))
with gr.Column(scale=1):
summary_outputs.append(gr.Textbox(
label=f"요약 결과 {i+1}",
lines=2,
visible=False
))
classification_outputs.append(gr.HTML(
label=f"분류 결과 {i+1}",
visible=False
))
# 버튼 영역
with gr.Row():
with gr.Column(scale=1):
add_input_btn = gr.Button("입력 필드 추가", variant="secondary")
with gr.Column(scale=1):
remove_input_btn = gr.Button("입력 필드 제거", variant="secondary")
with gr.Column(scale=2):
batch_analyze_btn = gr.Button("일괄 분석", variant="primary")
# 입력 필드 추가 함수
def add_input_field():
i = len(analysis_items)
with gr.Group():
with gr.Row():
with gr.Column(scale=1):
new_input = gr.Textbox(
label=f"사고 경위 {i+1}",
placeholder="여기에 사고 경위를 입력하세요...",
lines=3
)
analysis_items.append(new_input)
with gr.Column(scale=1):
new_summary = gr.Textbox(
label=f"요약 결과 {i+1}",
lines=2,
visible=False
)
summary_outputs.append(new_summary)
new_classification = gr.HTML(
label=f"분류 결과 {i+1}",
visible=False
)
classification_outputs.append(new_classification)
# 모든 컴포넌트를 반환
return analysis_items + summary_outputs + classification_outputs
# 입력 필드 제거 함수
def remove_input_field():
if len(analysis_items) > 1: # 최소 1개 필드는 유지
analysis_items.pop()
summary_outputs.pop()
classification_outputs.pop()
# 모든 컴포넌트를 반환
return analysis_items + summary_outputs + classification_outputs
# 이벤트 연결
add_input_btn.click(
fn=add_input_field,
inputs=[],
outputs=analysis_items + summary_outputs + classification_outputs
)
remove_input_btn.click(
fn=remove_input_field,
inputs=[],
outputs=analysis_items + summary_outputs + classification_outputs
)
# 분석 버튼 클릭 이벤트 연결
batch_analyze_btn.click(
fn=analyze_multiple_fields,
inputs=analysis_items,
outputs=summary_outputs + classification_outputs
)
gr.Markdown(
"""
### 📊 다중 사고 분석 사용법
1. 각 사고 경위를 개별 입력 상자에 입력하세요.
2. 필요에 따라 '입력 필드 추가' 버튼으로 더 많은 사고 경위를 추가할 수 있습니다.
3. '일괄 분석' 버튼을 클릭하여 모든 사고를 한 번에 분석하세요.
4. 각 사고에 대한 요약과 사고 유형 분류 결과가 각 입력 필드 아래에 표시됩니다.
""")
# 탭 3: 엑셀 파일 분석
with gr.TabItem("엑셀 파일 분석"):
gr.Markdown("### 📑 엑셀 파일을 통한 다중 사고 분석")
gr.Markdown("""
**양식 안내**: 엑셀 파일에는 '사고경위' 열이 포함되어야 합니다.
각 행에 분석할 사고 경위를 입력한 후 업로드해 주세요.
""")
# 양식 다운로드 부분
with gr.Row():
excel_template_btn = gr.Button("엑셀 양식 다운로드", variant="secondary")
template_output = gr.File(label="양식 파일") # visible=False 제거
excel_template_btn.click(
fn=download_excel_template,
inputs=[],
outputs=[template_output]
)
# 파일 업로드 및 분석 부분
with gr.Row():
with gr.Column():
excel_input = gr.File(
label="사고 경위 엑셀 파일 업로드 (.xlsx)",
file_types=[".xlsx"]
)
excel_analyze_btn = gr.Button("엑셀 파일 분석", variant="primary")
with gr.Column():
excel_result = gr.File(label="분석 결과 파일")
excel_error = gr.Textbox(label="처리 상태", placeholder="파일을 업로드하고 분석 버튼을 클릭하세요.")
excel_analyze_btn.click(
fn=analyze_excel_file,
inputs=[excel_input],
outputs=[excel_result, excel_error]
)
gr.Markdown(
"""
### 📑 엑셀 파일 분석 사용법
1. '엑셀 양식 다운로드' 버튼을 클릭하여 템플릿을 다운로드하세요.
2. 템플릿에 사고 경위를 입력하고 저장하세요.
3. 저장된 파일을 업로드하세요.
4. '엑셀 파일 분석' 버튼을 클릭하여 결과 파일을 생성하세요.
5. 생성된 결과 파일을 다운로드하여 확인하세요.
"""
)
gr.Markdown(
"""
## 📊 사용 방법
- **개별 사고 분석**: 단일 사고 경위를 입력하고 상세 분석 결과를 확인합니다.
- **다중 사고 분석**: 여러 사고 경위를 한 번에 입력하여 일괄 분석합니다.
- **엑셀 파일 분석**: 다수의 사고 경위가 포함된 엑셀 파일을 업로드하여 분석합니다.
## ℹ️ 모델 정보
- 분류 모델: {classification_repo}
- 요약 모델: {summarization_repo}
""".format(
classification_repo=MODEL_CONFIG['classification_repo'],
summarization_repo=MODEL_CONFIG['summarization_repo'] if MODEL_CONFIG['summarization_repo'] else "사용 안 함"
)
)
return app
# 메인 함수
def main():
"""메인 실행 함수"""
# 모델 미리 로드 (첫번째 요청 속도 개선)
try:
_ = get_model()
print("모델 사전 로드 완료!")
except Exception as e:
print(f"모델 사전 로드 실패: {e}")
print("첫 요청 시 모델을 로드합니다.")
# Gradio 앱 생성 및 실행
app = create_interface()
app.launch(share=False)
if __name__ == "__main__":
main()