Yrdksam's picture
Update app.py
0dd49b6 verified
import json
import numpy as np
import torch
from flask import Flask, request, jsonify, render_template
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from sentence_transformers import SentenceTransformer
import os
# --- μ„€μ • ---
VERSION = "v1.5" # λ˜λŠ” v1.3 λ“± μ‚¬μš©ν•˜μ‹œλŠ” 버전에 맞게 μˆ˜μ •
TOP_K = 3
DEFAULT_EMOTION_WEIGHT = 0.8
DEFAULT_CONTEXT_WEIGHT = 0.2
# --- 파일 경둜 μ •μ˜ ---
EMOTION_MODEL_PATH = f'./my_emotion_model_{VERSION}'
MUSIC_DB_SONG_INFO_PATH = f'./lyrics_recommend/song_database_{VERSION}.json'
MUSIC_DB_EMOTION_VECTORS_PATH = f'./lyrics_recommend/lyrics_emotion_embeddings_{VERSION}.npy'
MUSIC_DB_CONTEXT_VECTORS_PATH = f'./lyrics_recommend/lyrics_context_embeddings_{VERSION}.npy'
FLOWER_DB_FLOWER_INFO_PATH = f'./flower_recommend/flower_database_{VERSION}.json'
FLOWER_DB_EMOTION_VECTORS_PATH = f'./flower_recommend/flower_emotion_embeddings_{VERSION}.npy'
FLOWER_DB_CONTEXT_VECTORS_PATH = f'./flower_recommend/flower_context_embeddings_{VERSION}.npy'
# --- λͺ¨λΈ 및 데이터 λ‘œλ“œ ---
print("μ„œλ²„ μ‹œμž‘: λͺ¨λΈκ³Ό λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό λ‘œλ“œν•©λ‹ˆλ‹€...")
try:
emotion_tokenizer = AutoTokenizer.from_pretrained(EMOTION_MODEL_PATH)
emotion_model = AutoModelForSequenceClassification.from_pretrained(EMOTION_MODEL_PATH, output_hidden_states=True)
emotion_model.eval()
## BM-K/KoSimCSE-roberta or jhgan/ko-sbert-nli
context_model = SentenceTransformer('BM-K/KoSimCSE-roberta')
music_emotion_vectors = np.load(MUSIC_DB_EMOTION_VECTORS_PATH)
music_context_vectors = np.load(MUSIC_DB_CONTEXT_VECTORS_PATH)
with open(MUSIC_DB_SONG_INFO_PATH, 'r', encoding='utf-8') as f:
music_db_info = json.load(f)
print("- μŒμ•… DB λ‘œλ“œ μ™„λ£Œ!")
flower_emotion_vectors = np.load(FLOWER_DB_EMOTION_VECTORS_PATH)
flower_context_vectors = np.load(FLOWER_DB_CONTEXT_VECTORS_PATH)
with open(FLOWER_DB_FLOWER_INFO_PATH, 'r', encoding='utf-8') as f:
flower_db_info = json.load(f)
print("- 꽃 DB λ‘œλ“œ μ™„λ£Œ!")
except FileNotFoundError as e:
print(f"였λ₯˜: ν•„μˆ˜ νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. ({e})")
print("λͺ¨λ“  파일이 app.py와 같은 폴더 ꡬ쑰에 μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”.")
exit()
# --- Flask μ•± 생성 ---
app = Flask(__name__)
def cosine_similarity(vec1, vec2_matrix):
vec1_norm = np.linalg.norm(vec1)
vec2_matrix_norm = np.linalg.norm(vec2_matrix, axis=1)
epsilon = 1e-8
return np.dot(vec2_matrix, vec1) / (vec1_norm * vec2_matrix_norm + epsilon)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/flower')
def flower_page():
return render_template('flower.html')
@app.route('/recommend_music', methods=['POST'])
def recommend_music_api():
data = request.json
user_input = data.get('text')
emotion_weight = data.get('emotion_weight', 0.8)
context_weight = data.get('context_weight', 0.2)
if not user_input:
return jsonify({"error": "ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”."}), 400
inputs = emotion_tokenizer(user_input, return_tensors="pt", max_length=128, truncation=True)
with torch.no_grad():
outputs = emotion_model(**inputs)
user_emotion_label = emotion_model.config.id2label[torch.argmax(outputs.logits, dim=-1).item()]
user_emotion_vector = outputs.hidden_states[-1][:, 0, :].numpy().flatten()
user_context_vector = context_model.encode(user_input)
candidate_indices = [i for i, song in enumerate(music_db_info) ]
##if song['emotion'] == user_emotion_label
if not candidate_indices:
return jsonify({"emotion": user_emotion_label, "recommendations": []})
cand_emotion_vectors = music_emotion_vectors[candidate_indices]
cand_context_vectors = music_context_vectors[candidate_indices]
emotion_sims = cosine_similarity(user_emotion_vector, cand_emotion_vectors)
context_sims = cosine_similarity(user_context_vector, cand_context_vectors)
total_scores = (emotion_weight * emotion_sims) + (context_weight * context_sims)
sorted_indices = np.argsort(total_scores)[::-1]
recommendations = []
for i in range(min(TOP_K, len(sorted_indices))):
original_idx = candidate_indices[sorted_indices[i]]
song = music_db_info[original_idx]
score = total_scores[sorted_indices[i]]
recommendations.append({
"rank": i + 1, "artist": song['artist'], "title": song['title'],
"score": float(score), "videoId": song.get('videoId', '')
})
return jsonify({"emotion": user_emotion_label, "recommendations": recommendations})
@app.route('/recommend_flower', methods=['POST'])
def recommend_flower_api():
data = request.json
user_input = data.get('text')
emotion_weight = data.get('emotion_weight', 0.2)
context_weight = data.get('context_weight', 0.8)
if not user_input:
return jsonify({"error": "ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”."}), 400
inputs = emotion_tokenizer(user_input, return_tensors="pt", max_length=128, truncation=True)
with torch.no_grad():
outputs = emotion_model(**inputs)
user_emotion_label = emotion_model.config.id2label[torch.argmax(outputs.logits, dim=-1).item()]
user_emotion_vector = outputs.hidden_states[-1][:, 0, :].numpy().flatten()
user_context_vector = context_model.encode(user_input)
candidate_indices = [i for i, flower in enumerate(flower_db_info) if flower['emotion'] == user_emotion_label]
if not candidate_indices:
return jsonify({"emotion": user_emotion_label, "recommendations": []})
cand_emotion_vectors = flower_emotion_vectors[candidate_indices]
cand_context_vectors = flower_context_vectors[candidate_indices]
emotion_sims = cosine_similarity(user_emotion_vector, cand_emotion_vectors)
context_sims = cosine_similarity(user_context_vector, cand_context_vectors)
total_scores = (emotion_weight * emotion_sims) + (context_weight * context_sims)
sorted_indices = np.argsort(total_scores)[::-1]
recommendations = []
for i in range(min(TOP_K, len(sorted_indices))):
original_idx = candidate_indices[sorted_indices[i]]
flower = flower_db_info[original_idx]
score = total_scores[sorted_indices[i]]
recommendations.append({
"rank": i + 1, "name": flower['name'], "meaning": flower['words'],
"score": float(score)
})
return jsonify({"emotion": user_emotion_label, "recommendations": recommendations})
# Hugging Face SpacesλŠ” 기본적으둜 7860 포트λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, debug=True)