PHONG NGUYỄN THANH commited on
Commit
0226beb
·
unverified ·
2 Parent(s): 5be9894 0490e30

Merge pull request #1 from T-Phong/master

Browse files
Files changed (5) hide show
  1. .gitignore +23 -0
  2. Import.xlsx +0 -0
  3. api.py +105 -0
  4. model.py +42 -0
  5. requirements.txt +6 -0
.gitignore CHANGED
@@ -205,3 +205,26 @@ cython_debug/
205
  marimo/_static/
206
  marimo/_lsp/
207
  __marimo__/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  marimo/_static/
206
  marimo/_lsp/
207
  __marimo__/
208
+ # Byte-compiled / optimized / DLL files
209
+ __pycache__/
210
+ *.py[cod]
211
+ *$py.class
212
+
213
+ # C extensions
214
+ *.so
215
+
216
+ # Distribution / packaging
217
+ build/
218
+ dist/
219
+ *.egg-info/
220
+
221
+ # Virtual Environments
222
+ .env
223
+ .venv
224
+ env/
225
+ venv/
226
+
227
+ # Hugging Face cache
228
+ # C�c model du?c t?i t? Hub, kh�ng c?n luu tr? trong git
229
+ .cache/
230
+ huggingface/
Import.xlsx ADDED
Binary file (28.8 kB). View file
 
api.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ from flask_cors import CORS
3
+ from model import predict_sentiment_3sentiment, predict_sentiment_5sentiment
4
+ import pandas as pd
5
+
6
+ # Khởi tạo Flask app
7
+ app = Flask(__name__)
8
+ CORS(app)
9
+
10
+ # Xử lý encoding cho tiếng Việt để hiển thị đúng trong response
11
+ app.config['JSON_AS_ASCII'] = False
12
+
13
+ @app.route("/predict", methods=['POST'])
14
+ def predict():
15
+ """
16
+ Dự đoán cảm xúc từ văn bản đầu vào.
17
+ Request body phải là JSON có dạng: {"text": "nội dung bình luận"}
18
+ """
19
+ # Lấy dữ liệu JSON từ request
20
+ json_data = request.get_json()
21
+
22
+ # Kiểm tra xem key 'text' có tồn tại và không rỗng không
23
+ if not json_data or 'text' not in json_data or not json_data.get('text', '').strip():
24
+ return jsonify({"error": "Vui lòng cung cấp trường 'text' trong request body."}), 400
25
+ # Kiểm tra xem key 'type' có tồn tại và không rỗng không
26
+ if not json_data or 'type' not in json_data or not json_data.get('type', '').strip():
27
+ return jsonify({"error": "Vui lòng cung cấp trường 'type' trong request body."}), 400
28
+
29
+ # Lấy văn bản từ dữ liệu
30
+ text_to_predict = json_data['text']
31
+ sentiment_type = json_data['type']
32
+ # Gọi hàm dự đoán từ model
33
+ if sentiment_type == "3sentiment":
34
+ sentiment, score = predict_sentiment_3sentiment(text_to_predict)
35
+ elif sentiment_type == "5sentiment":
36
+ sentiment, score = predict_sentiment_5sentiment(text_to_predict)
37
+
38
+ # Tạo response
39
+ response = {
40
+ "comment": text_to_predict,
41
+ "sentiment": sentiment,
42
+ "confidence": score
43
+ }
44
+
45
+ return jsonify(response)
46
+
47
+ @app.route("/")
48
+ def read_root():
49
+ return jsonify({"message": "Chào mừng đến với API Phân tích Cảm xúc sử dụng Flask!"})
50
+
51
+ @app.route("/predict-batch", methods=['POST'])
52
+ def predict_batch():
53
+ """
54
+ Dự đoán cảm xúc cho một loạt bình luận từ file Excel.
55
+ File Excel phải được gửi dưới dạng form-data với key là 'file'.
56
+ Cột đầu tiên của file Excel sẽ được sử dụng làm cột chứa bình luận.
57
+ """
58
+ # 1. Kiểm tra xem có file trong request không
59
+ if 'file' not in request.files:
60
+ return jsonify({"error": "Không tìm thấy file trong request (key phải là 'file')."}), 400
61
+
62
+ # Dữ liệu form đi kèm với file sẽ nằm trong request.form
63
+ sentiment_type = request.form.get('type')
64
+
65
+ # Kiểm tra xem key 'type' có tồn tại và không rỗng không
66
+ if not sentiment_type or sentiment_type.strip() not in ["3sentiment", "5sentiment"]:
67
+ return jsonify({"error": "Vui lòng cung cấp trường 'type' (3sentiment hoặc 5sentiment) trong form data."}), 400
68
+ file = request.files['file']
69
+
70
+ # 2. Kiểm tra xem người dùng có chọn file không
71
+ if file.filename == '':
72
+ return jsonify({"error": "Chưa chọn file nào."}), 400
73
+
74
+ # 3. Kiểm tra định dạng file
75
+ if not file.filename.endswith(('.xlsx', '.xls')):
76
+ return jsonify({"error": "Định dạng file không hợp lệ. Vui lòng sử dụng file .xlsx hoặc .xls."}), 400
77
+
78
+ try:
79
+ # 4. Đọc file Excel bằng pandas, sử dụng engine openpyxl
80
+ df = pd.read_excel(file, engine='openpyxl')
81
+
82
+ if df.empty:
83
+ return jsonify({"error": "File Excel rỗng."}), 400
84
+
85
+ # Lấy tên cột đầu tiên để xử lý
86
+ comments_column = df.columns[0]
87
+
88
+ results = []
89
+ # 5. Lặp qua từng bình luận (bỏ qua các dòng rỗng) để dự đoán
90
+ for comment in df[comments_column].dropna().astype(str):
91
+ if sentiment_type == "3sentiment":
92
+ sentiment, score = predict_sentiment_3sentiment(comment)
93
+ elif sentiment_type == "5sentiment":
94
+ sentiment, score = predict_sentiment_5sentiment(comment)
95
+ results.append({"comment": comment, "sentiment": sentiment, "confidence": score})
96
+
97
+ return jsonify(results)
98
+ except Exception as e:
99
+ return jsonify({"error": f"Đã xảy ra lỗi khi xử lý file: {str(e)}"}), 500
100
+
101
+ if __name__ == "__main__":
102
+ # Chạy app ở chế độ debug để tự động reload khi có thay đổi
103
+ # host='0.0.0.0' để có thể truy cập từ bên ngoài network
104
+ # Lưu ý: Không sử dụng debug=True trong môi trường production
105
+ app.run(host="0.0.0.0", port=8000, debug=True)
model.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Thay bằng repo_id bạn đã tạo ở trên
3
+ import torch
4
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer
5
+ import torch.nn.functional as F
6
+
7
+ model_name_3sentiment = "phongnt251199/phobert-sentiment-reviews-v5"
8
+ model_name_5sentiment = "phongnt251199/phobert-sentiment-reviews-v4"
9
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
10
+
11
+ # Load tokenizer và model cho 3 nhãn
12
+ tokenizer_3sentiment = AutoTokenizer.from_pretrained(model_name_3sentiment)
13
+ model_3sentiment = AutoModelForSequenceClassification.from_pretrained(model_name_3sentiment, num_labels=3).to(device)
14
+
15
+ # Load tokenizer và model như bình thườngclear
16
+ tokenizer_5sentiment = AutoTokenizer.from_pretrained(model_name_5sentiment)
17
+ model_5sentiment = AutoModelForSequenceClassification.from_pretrained(model_name_5sentiment, num_labels=5).to(device)
18
+
19
+ def predict_sentiment_3sentiment(text):
20
+ inputs = tokenizer_3sentiment(text, padding=True, truncation=True, max_length=256, return_tensors="pt").to(device)
21
+ with torch.no_grad():
22
+ outputs = model_3sentiment(**inputs)
23
+ probs = outputs.logits.softmax(dim=1)
24
+ pred_label = torch.argmax(probs, dim=1).item()
25
+ # Dòng sau dùng để debug, có thể xóa hoặc comment lại
26
+ # probs_point = F.softmax(outputs.logits, dim=1)
27
+ # print(probs_point[0])
28
+ #labels_map = {0: 'Rất tệ', 1: 'Tệ', 2: 'Bình thường', 3: 'Khá tốt', 4: 'Rất tốt'}
29
+ labels_map = {0: 'Tiêu cực', 1: 'Bình thường', 2: 'Tích cực'}
30
+ return labels_map[pred_label], probs[0][pred_label].item()
31
+
32
+ def predict_sentiment_5sentiment(text):
33
+ inputs = tokenizer_5sentiment(text, padding=True, truncation=True, max_length=256, return_tensors="pt").to(device)
34
+ with torch.no_grad():
35
+ outputs = model_5sentiment(**inputs)
36
+ probs = outputs.logits.softmax(dim=1)
37
+ pred_label = torch.argmax(probs, dim=1).item()
38
+ # Dòng sau dùng để debug, có thể xóa hoặc comment lại
39
+ # probs_point = F.softmax(outputs.logits, dim=1)
40
+ # print(probs_point[0])
41
+ labels_map = {0: 'Rất tệ (1 sao)', 1: 'Tệ (2 sao)', 2: 'Bình thường (3 sao)', 3: 'Khá tốt (4 sao)', 4: 'Rất tốt (5 sao)'}
42
+ return labels_map[pred_label], probs[0][pred_label].item()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ transformers
2
+ torch
3
+ flask
4
+ pandas
5
+ openpyxl
6
+ flask-cors