Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
3 |
+
import time
|
4 |
+
|
5 |
+
# Page configuration
|
6 |
+
st.set_page_config(
|
7 |
+
page_title="Tóm tắt văn bản tiếng Việt",
|
8 |
+
page_icon="📝",
|
9 |
+
layout="wide",
|
10 |
+
initial_sidebar_state="expanded"
|
11 |
+
)
|
12 |
+
|
13 |
+
# Custom CSS for better styling
|
14 |
+
st.markdown("""
|
15 |
+
<style>
|
16 |
+
.main-header {
|
17 |
+
text-align: center;
|
18 |
+
padding: 2rem 0;
|
19 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
20 |
+
color: white;
|
21 |
+
border-radius: 10px;
|
22 |
+
margin-bottom: 2rem;
|
23 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
24 |
+
}
|
25 |
+
|
26 |
+
.feature-card {
|
27 |
+
background: white;
|
28 |
+
padding: 1.5rem;
|
29 |
+
border-radius: 10px;
|
30 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
31 |
+
margin: 1rem 0;
|
32 |
+
border-left: 4px solid #667eea;
|
33 |
+
}
|
34 |
+
|
35 |
+
.stats-card {
|
36 |
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
37 |
+
color: white;
|
38 |
+
padding: 1rem;
|
39 |
+
border-radius: 8px;
|
40 |
+
text-align: center;
|
41 |
+
margin: 0.5rem 0;
|
42 |
+
}
|
43 |
+
|
44 |
+
.summary-container {
|
45 |
+
background: #f8f9fa;
|
46 |
+
padding: 1.5rem;
|
47 |
+
border-radius: 10px;
|
48 |
+
border: 1px solid #e9ecef;
|
49 |
+
margin: 1rem 0;
|
50 |
+
}
|
51 |
+
|
52 |
+
.footer {
|
53 |
+
text-align: center;
|
54 |
+
padding: 2rem 0;
|
55 |
+
color: #6c757d;
|
56 |
+
border-top: 1px solid #e9ecef;
|
57 |
+
margin-top: 3rem;
|
58 |
+
}
|
59 |
+
|
60 |
+
.sidebar-info {
|
61 |
+
background: #f8f9fa;
|
62 |
+
padding: 1rem;
|
63 |
+
border-radius: 8px;
|
64 |
+
margin: 1rem 0;
|
65 |
+
}
|
66 |
+
</style>
|
67 |
+
""", unsafe_allow_html=True)
|
68 |
+
|
69 |
+
# Header
|
70 |
+
st.markdown("""
|
71 |
+
<div class="main-header">
|
72 |
+
<h1>📝 Tóm tắt văn bản tiếng Việt với AI</h1>
|
73 |
+
<p style="font-size: 1.2em; margin-top: 0.5rem;">Powered by VietAI T5 Model</p>
|
74 |
+
</div>
|
75 |
+
""", unsafe_allow_html=True)
|
76 |
+
|
77 |
+
# Sidebar
|
78 |
+
with st.sidebar:
|
79 |
+
st.markdown("## ⚙️ Cài đặt")
|
80 |
+
|
81 |
+
# Model information
|
82 |
+
st.markdown("""
|
83 |
+
<div class="sidebar-info">
|
84 |
+
<h4>🤖 Thông tin Model</h4>
|
85 |
+
<p><strong>Model:</strong> VietAI/vit5-base-vietnews-summarization</p>
|
86 |
+
<p><strong>Ngôn ngữ:</strong> Tiếng Việt</p>
|
87 |
+
<p><strong>Loại:</strong> Text-to-Text Generation</p>
|
88 |
+
</div>
|
89 |
+
""", unsafe_allow_html=True)
|
90 |
+
|
91 |
+
# Summarization settings
|
92 |
+
st.markdown("### 📊 Tùy chỉnh tóm tắt")
|
93 |
+
max_length = st.slider("Độ dài tóm tắt tối đa", 50, 300, 150)
|
94 |
+
min_length = st.slider("Độ dài tóm tắt tối thiểu", 20, 100, 40)
|
95 |
+
num_beams = st.selectbox("Số beam search", [2, 4, 6, 8], index=1)
|
96 |
+
|
97 |
+
# Tips
|
98 |
+
st.markdown("""
|
99 |
+
<div class="sidebar-info">
|
100 |
+
<h4>💡 Mẹo sử dụng</h4>
|
101 |
+
<ul>
|
102 |
+
<li>Văn bản từ 100-1000 từ cho kết quả tốt nhất</li>
|
103 |
+
<li>Sử dụng văn bản tiếng Việt chính thống</li>
|
104 |
+
<li>Tránh văn bản có quá nhiều từ viết tắt</li>
|
105 |
+
</ul>
|
106 |
+
</div>
|
107 |
+
""", unsafe_allow_html=True)
|
108 |
+
|
109 |
+
# Load model with progress
|
110 |
+
@st.cache_resource
|
111 |
+
def load_model():
|
112 |
+
with st.spinner("🔄 Đang tải model..."):
|
113 |
+
tokenizer = AutoTokenizer.from_pretrained("VietAI/vit5-base-vietnews-summarization")
|
114 |
+
model = AutoModelForSeq2SeqLM.from_pretrained("VietAI/vit5-base-vietnews-summarization")
|
115 |
+
return tokenizer, model
|
116 |
+
|
117 |
+
# Main content area
|
118 |
+
col1, col2 = st.columns([2, 1])
|
119 |
+
|
120 |
+
with col1:
|
121 |
+
st.markdown("## ✍️ Nhập văn bản cần tóm tắt")
|
122 |
+
|
123 |
+
# Sample texts
|
124 |
+
sample_texts = {
|
125 |
+
"Chọn văn bản mẫu...": "",
|
126 |
+
"Tin tức công nghệ": "Công nghệ trí tuệ nhân tạo (AI) đang phát triển với tốc độ chóng mặt và tạo ra những thay đổi sâu sắc trong nhiều lĩnh vực của đời sống. Từ y tế, giáo dục, giao thông đến tài chính, AI đang được ứng dụng rộng rãi để cải thiện hiệu quả và chất lượng dịch vụ. Tuy nhiên, sự phát triển nhanh chóng này cũng đặt ra nhiều thách thức về đạo đức, bảo mật thông tin và tác động đến thị trường lao động.",
|
127 |
+
"Tin tức kinh tế": "Nền kinh tế Việt Nam trong quý đầu năm ghi nhận nhiều tín hiệu tích cực với tốc độ tăng trưởng GDP đạt mức khả quan. Xuất khẩu tiếp tục là động lực chính thúc đẩy tăng trưởng, đặc biệt trong các ngành công nghiệp chế biến chế tạo và nông sản. Tuy nhiên, lạm phát vẫn là thách thức cần được kiểm soát chặt chẽ để đảm bảo ổn định kinh tế vĩ mô."
|
128 |
+
}
|
129 |
+
|
130 |
+
selected_sample = st.selectbox("Hoặc chọn văn bản mẫu:", list(sample_texts.keys()))
|
131 |
+
|
132 |
+
if selected_sample != "Chọn văn bản mẫu...":
|
133 |
+
text = st.text_area("", value=sample_texts[selected_sample], height=300)
|
134 |
+
else:
|
135 |
+
text = st.text_area("", height=300, placeholder="Nhập hoặc dán văn bản tiếng Việt vào đây...")
|
136 |
+
|
137 |
+
with col2:
|
138 |
+
st.markdown("## 📈 Thống kê văn bản")
|
139 |
+
|
140 |
+
if text.strip():
|
141 |
+
word_count = len(text.split())
|
142 |
+
char_count = len(text)
|
143 |
+
|
144 |
+
st.markdown(f"""
|
145 |
+
<div class="stats-card">
|
146 |
+
<h3>{word_count}</h3>
|
147 |
+
<p>Số từ</p>
|
148 |
+
</div>
|
149 |
+
""", unsafe_allow_html=True)
|
150 |
+
|
151 |
+
st.markdown(f"""
|
152 |
+
<div class="stats-card">
|
153 |
+
<h3>{char_count}</h3>
|
154 |
+
<p>Số ký tự</p>
|
155 |
+
</div>
|
156 |
+
""", unsafe_allow_html=True)
|
157 |
+
|
158 |
+
estimated_time = max(1, word_count // 100)
|
159 |
+
st.markdown(f"""
|
160 |
+
<div class="stats-card">
|
161 |
+
<h3>~{estimated_time}s</h3>
|
162 |
+
<p>Thời gian ước tính</p>
|
163 |
+
</div>
|
164 |
+
""", unsafe_allow_html=True)
|
165 |
+
|
166 |
+
# Load model
|
167 |
+
tokenizer, model = load_model()
|
168 |
+
|
169 |
+
# Summarization button and result
|
170 |
+
st.markdown("---")
|
171 |
+
|
172 |
+
col1, col2, col3 = st.columns([1, 1, 1])
|
173 |
+
with col2:
|
174 |
+
if st.button("🔍 Tóm tắt văn bản", type="primary", use_container_width=True):
|
175 |
+
if text.strip() == "":
|
176 |
+
st.warning("⚠️ Vui lòng nhập văn bản trước khi tóm tắt.")
|
177 |
+
else:
|
178 |
+
# Progress bar
|
179 |
+
progress_bar = st.progress(0)
|
180 |
+
status_text = st.empty()
|
181 |
+
|
182 |
+
start_time = time.time()
|
183 |
+
|
184 |
+
try:
|
185 |
+
status_text.text("🔄 Đang xử lý văn bản...")
|
186 |
+
progress_bar.progress(25)
|
187 |
+
|
188 |
+
# Tokenize input
|
189 |
+
input_ids = tokenizer.encode(
|
190 |
+
"summarize: " + text,
|
191 |
+
return_tensors="pt",
|
192 |
+
max_length=512,
|
193 |
+
truncation=True
|
194 |
+
)
|
195 |
+
progress_bar.progress(50)
|
196 |
+
|
197 |
+
status_text.text("🤖 Đang tạo tóm tắt...")
|
198 |
+
|
199 |
+
# Generate summary
|
200 |
+
output_ids = model.generate(
|
201 |
+
input_ids,
|
202 |
+
max_length=max_length,
|
203 |
+
min_length=min_length,
|
204 |
+
length_penalty=2.0,
|
205 |
+
num_beams=num_beams,
|
206 |
+
early_stopping=True
|
207 |
+
)
|
208 |
+
progress_bar.progress(75)
|
209 |
+
|
210 |
+
# Decode result
|
211 |
+
summary = tokenizer.decode(output_ids[0], skip_special_tokens=True)
|
212 |
+
progress_bar.progress(100)
|
213 |
+
|
214 |
+
end_time = time.time()
|
215 |
+
processing_time = round(end_time - start_time, 2)
|
216 |
+
|
217 |
+
status_text.text("✅ Hoàn thành!")
|
218 |
+
time.sleep(0.5)
|
219 |
+
status_text.empty()
|
220 |
+
progress_bar.empty()
|
221 |
+
|
222 |
+
# Display results
|
223 |
+
st.markdown("## 📋 Kết quả tóm tắt")
|
224 |
+
|
225 |
+
st.markdown(f"""
|
226 |
+
<div class="summary-container">
|
227 |
+
<h4>✅ Bản tóm tắt:</h4>
|
228 |
+
<p style="font-size: 1.1em; line-height: 1.6;">{summary}</p>
|
229 |
+
</div>
|
230 |
+
""", unsafe_allow_html=True)
|
231 |
+
|
232 |
+
# Summary statistics
|
233 |
+
col1, col2, col3, col4 = st.columns(4)
|
234 |
+
|
235 |
+
with col1:
|
236 |
+
st.metric("Văn bản gốc", f"{len(text.split())} từ")
|
237 |
+
|
238 |
+
with col2:
|
239 |
+
st.metric("Bản tóm tắt", f"{len(summary.split())} từ")
|
240 |
+
|
241 |
+
with col3:
|
242 |
+
compression_ratio = round((1 - len(summary.split()) / len(text.split())) * 100, 1)
|
243 |
+
st.metric("Tỷ lệ nén", f"{compression_ratio}%")
|
244 |
+
|
245 |
+
with col4:
|
246 |
+
st.metric("Thời gian xử lý", f"{processing_time}s")
|
247 |
+
|
248 |
+
# Copy to clipboard button
|
249 |
+
st.markdown("### 📋 Sao chép kết quả")
|
250 |
+
st.code(summary, language="text")
|
251 |
+
|
252 |
+
except Exception as e:
|
253 |
+
st.error(f"❌ Có lỗi xảy ra: {str(e)}")
|
254 |
+
progress_bar.empty()
|
255 |
+
status_text.empty()
|
256 |
+
|
257 |
+
# Features section
|
258 |
+
st.markdown("---")
|
259 |
+
st.markdown("## 🌟 Tính năng nổi bật")
|
260 |
+
|
261 |
+
col1, col2, col3 = st.columns(3)
|
262 |
+
|
263 |
+
with col1:
|
264 |
+
st.markdown("""
|
265 |
+
<div class="feature-card">
|
266 |
+
<h4>🎯 Độ chính xác cao</h4>
|
267 |
+
<p>Sử dụng mô hình VietAI T5 được huấn luyện chuyên biệt trên dữ liệu tiếng Việt</p>
|
268 |
+
</div>
|
269 |
+
""", unsafe_allow_html=True)
|
270 |
+
|
271 |
+
with col2:
|
272 |
+
st.markdown("""
|
273 |
+
<div class="feature-card">
|
274 |
+
<h4>⚡ Xử lý nhanh</h4>
|
275 |
+
<p>Tốc độ xử lý tối ưu với khả năng tóm tắt văn bản dài trong vài giây</p>
|
276 |
+
</div>
|
277 |
+
""", unsafe_allow_html=True)
|
278 |
+
|
279 |
+
with col3:
|
280 |
+
st.markdown("""
|
281 |
+
<div class="feature-card">
|
282 |
+
<h4>🔧 Tùy chỉnh linh hoạt</h4>
|
283 |
+
<p>Điều chỉnh độ dài và chất lượng tóm tắt theo nhu cầu cụ thể</p>
|
284 |
+
</div>
|
285 |
+
""", unsafe_allow_html=True)
|
286 |
+
|
287 |
+
# Footer
|
288 |
+
st.markdown("""
|
289 |
+
<div class="footer">
|
290 |
+
<p>📝 Vietnamese Text Summarization | Powered by VietAI T5 Model</p>
|
291 |
+
<p>Built with ❤️ using Streamlit and Hugging Face Transformers</p>
|
292 |
+
</div>
|
293 |
+
""", unsafe_allow_html=True)
|