Spaces:
Sleeping
Sleeping
# gradio final ver ---------------------------- | |
import torch | |
from torch import nn | |
import torch.nn.functional as F | |
import torch.optim as optim | |
from torch.utils.data import Dataset, DataLoader | |
import gluonnlp as nlp | |
import numpy as np | |
from tqdm import tqdm, tqdm_notebook | |
import pandas as pd | |
import ast | |
import os | |
# Hugging Face๋ฅผ ํตํ ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ Import | |
from kobert_tokenizer import KoBERTTokenizer | |
from transformers import BertModel | |
from transformers import AdamW | |
from transformers.optimization import get_cosine_schedule_with_warmup | |
n_devices = torch.cuda.device_count() | |
print(n_devices) | |
for i in range(n_devices): | |
print(torch.cuda.get_device_name(i)) | |
if torch.cuda.is_available(): | |
device = torch.device("cuda") | |
print('There are %d GPU(s) available.' % torch.cuda.device_count()) | |
print('We will use the GPU:', torch.cuda.get_device_name(0)) | |
else: | |
device = torch.device("cpu") | |
print('No GPU available, using the CPU instead.') | |
max_len = 64 | |
batch_size = 32 | |
warmup_ratio = 0.1 | |
num_epochs = 5 | |
max_grad_norm = 1 | |
log_interval = 200 | |
learning_rate = 1e-5 | |
class BERTSentenceTransform: | |
r"""BERT style data transformation. | |
Parameters | |
---------- | |
tokenizer : BERTTokenizer. | |
Tokenizer for the sentences. | |
max_seq_length : int. | |
Maximum sequence length of the sentences. | |
pad : bool, default True | |
Whether to pad the sentences to maximum length. | |
pair : bool, default True | |
Whether to transform sentences or sentence pairs. | |
""" | |
# ์ ๋ ฅ์ผ๋ก ๋ฐ์ tokenizerm ์ต๋ ์ํ์ค ๊ธธ์ด, vocab, pad ๋ฐ pair ์ค์ | |
def __init__(self, tokenizer, max_seq_length,vocab, pad=True, pair=True): | |
self._tokenizer = tokenizer | |
self._max_seq_length = max_seq_length | |
self._pad = pad | |
self._pair = pair | |
self._vocab = vocab | |
# ์ ๋ ฅ๋ ๋ฌธ์ฅ ๋๋ ๋ฌธ์ฅ ์์ BERT ๋ชจ๋ธ์ด ์ฌ์ฉํ ์ ์๋ ํ์์ผ๋ก ๋ณํ | |
def __call__(self, line): | |
"""Perform transformation for sequence pairs or single sequences. | |
The transformation is processed in the following steps: | |
- tokenize the input sequences | |
- insert [CLS], [SEP] as necessary | |
- generate type ids to indicate whether a token belongs to the first | |
sequence or the second sequence. | |
- generate valid length | |
For sequence pairs, the input is a tuple of 2 strings: | |
text_a, text_b. | |
Inputs: | |
text_a: 'is this jacksonville ?' | |
text_b: 'no it is not' | |
Tokenization: | |
text_a: 'is this jack ##son ##ville ?' | |
text_b: 'no it is not .' | |
Processed: | |
tokens: '[CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP]' | |
type_ids: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 | |
valid_length: 14 | |
For single sequences, the input is a tuple of single string: | |
text_a. | |
Inputs: | |
text_a: 'the dog is hairy .' | |
Tokenization: | |
text_a: 'the dog is hairy .' | |
Processed: | |
text_a: '[CLS] the dog is hairy . [SEP]' | |
type_ids: 0 0 0 0 0 0 0 | |
valid_length: 7 | |
Parameters | |
---------- | |
line: tuple of str | |
Input strings. For sequence pairs, the input is a tuple of 2 strings: | |
(text_a, text_b). For single sequences, the input is a tuple of single | |
string: (text_a,). | |
Returns | |
------- | |
np.array: input token ids in 'int32', shape (batch_size, seq_length) | |
np.array: valid length in 'int32', shape (batch_size,) | |
np.array: input token type ids in 'int32', shape (batch_size, seq_length) | |
""" | |
# convert to unicode | |
text_a = line[0] | |
if self._pair: | |
assert len(line) == 2 | |
text_b = line[1] | |
tokens_a = self._tokenizer.tokenize(text_a) | |
tokens_b = None | |
if self._pair: | |
tokens_b = self._tokenizer(text_b) | |
if tokens_b: | |
# Modifies `tokens_a` and `tokens_b` in place so that the total | |
# length is less than the specified length. | |
# Account for [CLS], [SEP], [SEP] with "- 3" | |
self._truncate_seq_pair(tokens_a, tokens_b, | |
self._max_seq_length - 3) | |
else: | |
# Account for [CLS] and [SEP] with "- 2" | |
if len(tokens_a) > self._max_seq_length - 2: | |
tokens_a = tokens_a[0:(self._max_seq_length - 2)] | |
# The embedding vectors for `type=0` and `type=1` were learned during | |
# pre-training and are added to the wordpiece embedding vector | |
# (and position vector). This is not *strictly* necessary since | |
# the [SEP] token unambiguously separates the sequences, but it makes | |
# it easier for the model to learn the concept of sequences. | |
# For classification tasks, the first vector (corresponding to [CLS]) is | |
# used as as the "sentence vector". Note that this only makes sense because | |
# the entire model is fine-tuned. | |
#vocab = self._tokenizer.vocab | |
vocab = self._vocab | |
tokens = [] | |
tokens.append(vocab.cls_token) | |
tokens.extend(tokens_a) | |
tokens.append(vocab.sep_token) | |
segment_ids = [0] * len(tokens) | |
if tokens_b: | |
tokens.extend(tokens_b) | |
tokens.append(vocab.sep_token) | |
segment_ids.extend([1] * (len(tokens) - len(segment_ids))) | |
input_ids = self._tokenizer.convert_tokens_to_ids(tokens) | |
# The valid length of sentences. Only real tokens are attended to. | |
valid_length = len(input_ids) | |
if self._pad: | |
# Zero-pad up to the sequence length. | |
padding_length = self._max_seq_length - valid_length | |
# use padding tokens for the rest | |
input_ids.extend([vocab[vocab.padding_token]] * padding_length) | |
segment_ids.extend([0] * padding_length) | |
return np.array(input_ids, dtype='int32'), np.array(valid_length, dtype='int32'),\ | |
np.array(segment_ids, dtype='int32') | |
class BERTDataset(Dataset): | |
def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer, vocab, max_len, | |
pad, pair): | |
transform = BERTSentenceTransform(bert_tokenizer, max_seq_length=max_len,vocab=vocab, pad=pad, pair=pair) | |
#transform = nlp.data.BERTSentenceTransform( | |
# tokenizer, max_seq_length=max_len, pad=pad, pair=pair) | |
self.sentences = [transform([i[sent_idx]]) for i in dataset] | |
self.labels = [np.int32(i[label_idx]) for i in dataset] | |
def __getitem__(self, i): | |
return (self.sentences[i] + (self.labels[i], )) | |
def __len__(self): | |
return (len(self.labels)) | |
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1') | |
bertmodel = BertModel.from_pretrained('skt/kobert-base-v1', return_dict=False) | |
vocab = nlp.vocab.BERTVocab.from_sentencepiece(tokenizer.vocab_file, padding_token='[PAD]') | |
# Kobert_softmax | |
class BERTClassifier(nn.Module): | |
def __init__(self, | |
bert, | |
hidden_size=768, | |
num_classes=6, | |
dr_rate=None, | |
params=None): | |
super(BERTClassifier, self).__init__() | |
self.bert = bert | |
self.dr_rate = dr_rate | |
self.softmax = nn.Softmax(dim=1) # Softmax๋ก ๋ณ๊ฒฝ | |
self.classifier = nn.Sequential( | |
nn.Dropout(p=0.5), | |
nn.Linear(in_features=hidden_size, out_features=512), | |
nn.Linear(in_features=512, out_features=num_classes), | |
) | |
# ์ ๊ทํ ๋ ์ด์ด ์ถ๊ฐ (Layer Normalization) | |
self.layer_norm = nn.LayerNorm(768) | |
# ๋๋กญ์์ | |
self.dropout = nn.Dropout(p=dr_rate) | |
def gen_attention_mask(self, token_ids, valid_length): | |
attention_mask = torch.zeros_like(token_ids) | |
for i, v in enumerate(valid_length): | |
attention_mask[i][:v] = 1 | |
return attention_mask.float() | |
def forward(self, token_ids, valid_length, segment_ids): | |
attention_mask = self.gen_attention_mask(token_ids, valid_length) | |
_, pooler = self.bert(input_ids=token_ids, token_type_ids=segment_ids.long(), attention_mask=attention_mask.float().to(token_ids.device)) | |
pooled_output = self.dropout(pooler) | |
normalized_output = self.layer_norm(pooled_output) | |
out = self.classifier(normalized_output) | |
# LayerNorm ์ ์ฉ | |
pooler = self.layer_norm(pooler) | |
if self.dr_rate: | |
pooler = self.dropout(pooler) | |
logits = self.classifier(pooler) # ๋ถ๋ฅ๋ฅผ ์ํ ๋ก์ง ๊ฐ ๊ณ์ฐ | |
probabilities = self.softmax(logits) # Softmax๋ก ๊ฐ ํด๋์ค์ ํ๋ฅ ๊ณ์ฐ | |
return probabilities # ๊ฐ ํด๋์ค์ ๋ํ ํ๋ฅ ๋ฐํ | |
model = torch.load('./model_weights_softmax(model).pth', map_location=torch.device('cpu')) | |
model.eval() | |
# ๋ฉ๋ก ๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ | |
melon_data = pd.read_csv('./melon_data.csv') | |
melon_emotions = pd.read_csv('./melon_emotions_final.csv') | |
melon_emotions = pd.merge(melon_emotions, melon_data, left_on='Title', right_on='title', how='inner') | |
melon_emotions = melon_emotions[['singer', 'Title', 'genre','Emotions']] | |
melon_emotions = melon_emotions.drop_duplicates(subset='Title', keep='first') | |
melon_emotions['Emotions'] = melon_emotions['Emotions'].apply(lambda x: ast.literal_eval(x)) | |
emotions = melon_emotions['Emotions'].to_list() | |
#gradio | |
import numpy as np | |
import pandas as pd | |
import requests | |
from PIL import Image | |
import torch | |
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification, pipeline | |
import gradio as gr | |
import openai | |
from sklearn.metrics.pairwise import cosine_similarity | |
import ast | |
###### ๊ธฐ๋ณธ ์ค์ ###### | |
# OpenAI API ํค ์ค์ | |
api_key = os.getenv("OPEN_AI_KEY") | |
openai.api_key = api_key | |
if openai.api_key: | |
print("Private Key:", openai.api_key) | |
else: | |
print("Private Key not set.") | |
# ๋ชจ๋ธ ๋ฐ ํ๋ก์ธ์ ๋ก๋ | |
processor = AutoProcessor.from_pretrained("openai/clip-vit-large-patch14") | |
model_clip = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-large-patch14") | |
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1') | |
# ์์ธก ๋ ์ด๋ธ | |
labels = ['a photo of a happy face', 'a photo of a joyful face', 'a photo of a loving face', | |
'a photo of an angry face', 'a photo of a melancholic face', 'a photo of a lonely face'] | |
###### ์ผ๊ตด ๊ฐ์ ๋ฒกํฐ ์์ธก ํจ์ ###### | |
def predict_face_emotion(image): | |
# ์ด๋ฏธ์ง๊ฐ None์ด๊ฑฐ๋ ์๋ชป๋ ๊ฒฝ์ฐ | |
if image is None: | |
return np.zeros(len(labels)) # ๋น ๋ฒกํฐ ๋ฐํ | |
# PIL ์ด๋ฏธ์ง๋ฅผ RGB๋ก ๋ณํ | |
image = image.convert("RGB") | |
# CLIP ๋ชจ๋ธ์ processor๋ฅผ ์ด์ฉํ ์ ์ฒ๋ฆฌ | |
inputs = processor(text=labels, images=image, return_tensors="pt", padding=True) | |
# pixel_values๊ฐ 4์ฐจ์์ธ์ง ํ์ธ ํ ๊ฐ์ ๋ณํ | |
pixel_values = inputs["pixel_values"] # (batch_size, channels, height, width) | |
# CLIP ๋ชจ๋ธ ์์ธก: forward์ ์ฌ๋ฐ๋ฅธ ์ ๋ ฅ ์ ๋ฌ | |
with torch.no_grad(): | |
outputs = model_clip(pixel_values=pixel_values, input_ids=inputs["input_ids"]) | |
# ํ๋ฅ ๊ฐ ๊ณ์ฐ | |
probs = outputs.logits_per_image.softmax(dim=1)[0] | |
return probs.numpy() | |
###### ํ ์คํธ ๊ฐ์ ๋ฒกํฐ ์์ธก ํจ์ ###### | |
sentence_emotions = [] | |
def predict_text_emotion(predict_sentence): | |
if not isinstance(predict_sentence, str): | |
predict_sentence = str(predict_sentence) | |
data = [predict_sentence, '0'] | |
dataset_another = [data] | |
another_test = BERTDataset(dataset_another, 0, 1, tokenizer, vocab, max_len, True, False) | |
test_dataloader = torch.utils.data.DataLoader(another_test, batch_size=1, num_workers=5) | |
model.eval() | |
for batch_id, (token_ids, valid_length, segment_ids, label) in enumerate(test_dataloader): | |
token_ids = token_ids.long().to(device) | |
segment_ids = segment_ids.long().to(device) | |
out = model(token_ids, valid_length, segment_ids) | |
for i in out: | |
logits = i.detach().cpu().numpy() | |
emotions = [value.item() for value in i] | |
sentence_emotions.append(emotions) | |
return sentence_emotions[0] # ์ต์ข ๋ฆฌ์คํธ ๋ฐํ | |
###### ์ต์ข ๊ฐ์ ๋ฒกํฐ ๊ณ์ฐ ###### | |
def generate_final_emotion_vector(diary_input, image_input): | |
# ํ ์คํธ ๊ฐ์ ๋ฒกํฐ ์์ธก | |
text_vector = predict_text_emotion(diary_input) | |
# ์ผ๊ตด ๊ฐ์ ๋ฒกํฐ ์์ธก | |
image_vector = predict_face_emotion(image_input) | |
text_vector = np.array(text_vector, dtype=float) | |
image_vector = np.array(image_vector, dtype=float) | |
print(text_vector) | |
print(image_vector) | |
# ์ต์ข ๊ฐ์ ๋ฒกํฐ ๊ฐ์ค์น ์ ์ฉ | |
return (text_vector * 0.7) + (image_vector * 0.3) | |
####### ์ฝ์ฌ์ธ ์ ์ฌ๋ ํจ์ ###### | |
def cosine_similarity_fn(vec1, vec2): | |
dot_product = np.dot(vec1, vec2) | |
norm_vec1 = np.linalg.norm(vec1) | |
norm_vec2 = np.linalg.norm(vec2) | |
if norm_vec1 == 0 or norm_vec2 == 0: | |
return np.nan # ์ ๋ก ๋ฒกํฐ์ธ ๊ฒฝ์ฐ NaN ๋ฐํ | |
return dot_product / (norm_vec1 * norm_vec2) | |
####### ์ด๋ฏธ์ง ๋ค์ด๋ก๋ ํจ์ (PIL ๊ฐ์ฒด ๋ฐํ) ###### | |
def download_image(image_url): | |
try: | |
response = requests.get(image_url) | |
response.raise_for_status() | |
return Image.open(requests.get(image_url, stream=True).raw) | |
except Exception as e: | |
print(f"์ด๋ฏธ์ง ๋ค์ด๋ก๋ ์ค๋ฅ: {e}") | |
return None | |
# ์คํ์ผ ์ต์ | |
options = { | |
1: "๐ผ ์น๊ทผํ", | |
2: "๐ฅ ํธ๋ ๋ํ MZ์ธ๋", | |
3: "๐ ์ ๋จธ๋ฌ์คํ ์ฅ๋๊พธ๋ฌ๊ธฐ", | |
4: "๐ง ์ฐจ๋ถํ ๋ช ์๊ฐ", | |
5: "๐จ ์ฐฝ์์ ์ธ ์์ ๊ฐ", | |
} | |
# ์ผ๊ธฐ ๋ถ์ ํจ์ | |
def chatbot_diary_with_image(style_option, diary_input, image_input, playlist_input): | |
style = options.get(int(style_option.split('.')[0]), "๐ผ ์น๊ทผํ") | |
# GPT ์๋ต (์ผ๊ธฐ ์ฝ๋ฉํธ) | |
try: | |
response_comment = openai.ChatCompletion.create( | |
model="gpt-4-turbo", | |
messages=[{"role": "system", "content": f"๋๋ {style} ์ฑ๋ด์ด์ผ."}, {"role": "user", "content": diary_input}], | |
) | |
comment = response_comment.choices[0].message.content | |
except Exception as e: | |
comment = f"๐ฌ ์ค๋ฅ: {e}" | |
# GPT ๊ธฐ๋ฐ ์ผ๊ธฐ ์ฃผ์ ์ถ์ฒ | |
try: | |
topics = get_initial_response(style_option, diary_input) | |
except Exception as e: | |
topics = f"๐ ์ฃผ์ ์ถ์ฒ ์ค๋ฅ: {e}" | |
# DALLยทE 3 ์ด๋ฏธ์ง ์์ฑ ์์ฒญ (3D ์คํ์ผ ์บ๋ฆญํฐ) | |
try: | |
response = openai.Image.create( | |
model="dall-e-3", | |
prompt=( | |
f"{diary_input}๋ฅผ ๋ฐ์ํด์ ๊ฐ์ ์ ํํํ๋ 3D ์คํ์ผ์ ์ผ๋ฌ์คํธ ์บ๋ฆญํฐ๋ฅผ ๊ทธ๋ ค์ค. " | |
"์บ๋ฆญํฐ๋ ๋ถ๋๋ฝ๊ณ ๋ฅ๊ทผ ๋์์ธ์ ํ์ ์ด ๊ฐ์ ์ ์ ๋๋ฌ๋ด์ผ ํด. " | |
"๊ฐ์ ์ ์๊ฐ์ ์ผ๋ก ํํํ ์ ์๋ ์ํ์ด๋ ์์ ์์ง์ ํฌํจํด์ค. " | |
"๊ฐ์ ์ ๋ถ์๊ธฐ๋ฅผ ๋ฐ์ํ๋ ์ ๋ช ํ๊ณ ๊นจ๋ํ ์์์ ์ฌ์ฉํ๊ณ , ์บ๋ฆญํฐ๊ฐ ์ญ๋์ ์ด๊ณ ์ฌ๋ฏธ์๋ ์์ธ๋ฅผ ์ทจํ ์ ์๋๋ก ํด์ค. " | |
"์ด๋ฏธ์ง์๋ ํ๋์ ์บ๋ฆญํฐ๋ง ๋์ค๊ฒ ํด์ค." | |
"๋ฐฐ๊ฒฝ์ ๋จ์ํ๊ณ ๋ฐ์ ์์์ผ๋ก ์ค์ ํด์ ์บ๋ฆญํฐ๊ฐ ๊ฐ์กฐ๋ ์ ์๋๋ก ํด์ค." | |
), | |
size="1024x1024", | |
n=1 | |
) | |
# URL ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ๋ค์ด๋ก๋ | |
image_url = response['data'][0]['url'] | |
print(f"Generated Image URL: {image_url}") # URL ํ์ธ | |
image = download_image(image_url) | |
except Exception as e: | |
print(f"์ด๋ฏธ์ง ์์ฑ ์ค๋ฅ: {e}") # ์ค๋ฅ ์์ธ ์ถ๋ ฅ | |
image = None | |
# ์ฌ์ฉ์ ์ต์ข ๊ฐ์ ๋ฒกํฐ | |
final_user_emotions = generate_final_emotion_vector(diary_input,image_input) | |
# ๊ฐ ๋ ธ๋์ ๋ํ ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ณ์ฐ | |
similarities = [cosine_similarity_fn(final_user_emotions, song_vec) for song_vec in emotions] | |
#์ ํจํ ์ ์ฌ๋ ํํฐ๋ง | |
valid_indices = [i for i, sim in enumerate(similarities) if not np.isnan(sim)] | |
filtered_similarities = [similarities[i] for i in valid_indices] | |
recommendations = np.argsort(filtered_similarities)[::-1] # ๋์ ์ ์ฌ๋ ์์ผ๋ก ์ ๋ ฌ | |
results_df = pd.DataFrame({ | |
'Singer' : melon_emotions['singer'].iloc[recommendations].values, | |
'title' : melon_emotions['Title'].iloc[recommendations].values, | |
'genre' : melon_emotions['genre'].iloc[recommendations].values, | |
'Cosine Similarity': [similarities[idx] for idx in recommendations] | |
}) | |
# ๊ฐ์ค์น ๊ฐ ์ค์ | |
gamma = 0.3 | |
similar_playlists = results_df.head(5) | |
similar_playlists = pd.merge(similar_playlists, melon_emotions, left_on="title", right_on="Title", how="inner") | |
similar_playlists = similar_playlists[["title", "Emotions", "singer"]] | |
dissimilar_playlists = results_df.tail(5) | |
dissimilar_playlists = pd.merge(dissimilar_playlists, melon_emotions, left_on="title", right_on="Title", how="inner") | |
dissimilar_playlists = dissimilar_playlists[["title", "Emotions", "singer"]] | |
#๊ฐ์ ๊ณผ ์ ์ฌํ ํ๋ ์ด๋ฆฌ์คํธ | |
if playlist_input == '๋น์ทํ': | |
results = [] | |
seen_songs = set(similar_playlists["title"].values) # ์ด๊ธฐ seen_songs์ similar_playlists์ ๊ณก๋ค์ ์ถ๊ฐ | |
# ์ฌ์ฉ์ ๊ฐ์ ๋ฒกํฐ | |
user_emotion_vector = generate_final_emotion_vector(diary_input, image_input).reshape(1, -1) | |
for index, row in similar_playlists.iterrows(): | |
song_title = row["title"] | |
song_singer = row["singer"] | |
song_vector = np.array(row["Emotions"]).reshape(1, -1) | |
song_results = [] | |
for i, emotion_vec in enumerate(emotions): | |
emotion_title = melon_emotions.iloc[i]["Title"] | |
emotion_singer = melon_emotions.iloc[i]["singer"] | |
emotion_vec = np.array(emotion_vec).reshape(1, -1) | |
# similar_playlists์ ์๋ ๊ณก๊ณผ seen_songs์ ์๋ ๊ณก์ ์ ์ธ | |
if ( | |
emotion_title != song_title and | |
emotion_title not in seen_songs | |
): | |
try: | |
# ๊ณก ๊ฐ ์ ์ฌ๋(Song-Song Similarity) | |
song_song_similarity = cosine_similarity(song_vector, emotion_vec)[0][0] | |
# ์ฌ์ฉ์ ๊ฐ์ ๋ฒกํฐ์์ ์ ์ฌ๋(User-Song Similarity) | |
user_song_similarity = cosine_similarity(user_emotion_vector, emotion_vec)[0][0] | |
# Final Score ๊ณ์ฐ | |
final_score = gamma * song_song_similarity + (1 - gamma) * user_song_similarity | |
song_results.append({ | |
"Title": emotion_title, | |
"Singer": emotion_singer, | |
"Song-Song Similarity": song_song_similarity, | |
"User-Song Similarity": user_song_similarity, | |
"Final Score": final_score | |
}) | |
except ValueError as e: | |
print(f"Error with {song_title} vs {emotion_title}: {e}") | |
continue | |
# Final Score๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ 3๊ณก ์ ํ | |
song_results = sorted(song_results, key=lambda x: x["Final Score"], reverse=True)[:3] | |
seen_songs.update([entry["Title"] for entry in song_results]) | |
results.append({"Song Title": song_title, "Singer": song_singer, "Top 3 Similarities": song_results}) | |
# ๊ฒฐ๊ณผ ์ถ๋ ฅ | |
for result in results: | |
print(f"{result['Singer']} - {result['Song Title']}") | |
for entry in result["Top 3 Similarities"]: | |
print(f"{entry['Singer']} - {entry['Title']} : Final Score {entry['Final Score']:.4f}") | |
print(f" (Song-Song Similarity: {entry['Song-Song Similarity']:.4f}, User-Song Similarity: {entry['User-Song Similarity']:.4f})") | |
print("-" * 30) | |
#๋ฐ๋ ํ๋ ์ด๋ฆฌ์คํธ | |
if playlist_input == '์๋ฐ๋': | |
results = [] | |
seen_songs = set() | |
# ์ฌ์ฉ์ ๊ฐ์ ๋ฒกํฐ | |
user_emotion_vector = generate_final_emotion_vector(diary_input, image_input).reshape(1, -1) | |
for index, row in dissimilar_playlists.iterrows(): | |
song_title = row["title"] | |
song_singer = row["singer"] | |
song_vector = np.array(row["Emotions"]).reshape(1, -1) | |
song_results = [] | |
for i, emotion_vec in enumerate(emotions): | |
emotion_title = melon_emotions.iloc[i]["Title"] | |
emotion_singer = melon_emotions.iloc[i]["singer"] | |
emotion_vec = np.array(emotion_vec).reshape(1, -1) | |
if ( | |
emotion_title != song_title and | |
emotion_title not in dissimilar_playlists["title"].values and | |
emotion_title not in seen_songs | |
): | |
try: | |
# ๊ณก ๊ฐ ์ ์ฌ๋(Song-Song Similarity) | |
song_song_similarity = cosine_similarity(song_vector, emotion_vec)[0][0] | |
# ์ฌ์ฉ์ ๊ฐ์ ๋ฒกํฐ์์ ๋ฐ๋ ์ ์ฌ๋(User-Song Dissimilarity) | |
opposite_user_song_similarity = 1 - cosine_similarity(user_emotion_vector, emotion_vec)[0][0] | |
# Final Score ๊ณ์ฐ | |
final_score = gamma * song_song_similarity + (1 - gamma) * opposite_user_song_similarity | |
song_results.append({ | |
"Title": emotion_title, | |
"Singer": emotion_singer, | |
"Song-Song Similarity": song_song_similarity, | |
"User-Song Dissimilarity": opposite_user_song_similarity, | |
"Final Score": final_score | |
}) | |
except ValueError as e: | |
print(f"Error with {song_title} vs {emotion_title}: {e}") | |
continue | |
# Final Score๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ 3๊ณก ์ ํ (๊ฐ์ด ํฐ ๊ณก์ด ๋ฐ๋๋๋ ๊ณก) | |
song_results = sorted(song_results, key=lambda x: x["Final Score"], reverse=True)[:3] | |
seen_songs.update(entry["Title"] for entry in song_results) | |
results.append({"Song Title": song_title, "Singer": song_singer, "Top 3 Similarities": song_results}) | |
# ๊ฒฐ๊ณผ ์ถ๋ ฅ | |
for result in results: | |
print(f"{result['Singer']} - {result['Song Title']}") | |
for entry in result["Top 3 Similarities"]: | |
print(f"{entry['Singer']} - {entry['Title']} : Final Score {entry['Final Score']:.4f}") | |
print(f' (Song-Song Similarity: {entry["Song-Song Similarity"]:.4f}, User-Song Dissimilarity: {entry["User-Song Dissimilarity"]:.4f})') | |
print("-" * 30) | |
# ๋ฐ์ดํฐํ๋ ์ ๋ณํ์ ์ํ ๋ฆฌ์คํธ ์์ฑ | |
df_rows = [] | |
for result in results: | |
song_title = result['Song Title'] | |
song_singer = result['Singer'] | |
main_song_info = f"{song_singer} - {song_title}" | |
for entry in result["Top 3 Similarities"]: | |
combined_info = f"{entry['Singer']} - {entry['Title']}" | |
df_rows.append({"1st ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ": main_song_info, "2nd ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ": combined_info}) | |
# ๋ฐ์ดํฐํ๋ ์ ์์ฑ | |
final_music_playlist_recommendation = pd.DataFrame(df_rows) | |
# ๊ณก ์ ๋ชฉ ๊ทธ๋ฃนํํ์ฌ ์ฒซ ๋ฒ์งธ ํ์๋ง ๊ณก ์ ๋ชฉ ํ์ | |
final_music_playlist_recommendation["1st ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ"] = final_music_playlist_recommendation.groupby("1st ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ")["1st ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ"].transform( | |
lambda x: [x.iloc[0]] + [""] * (len(x) - 1) | |
) | |
return final_music_playlist_recommendation, comment, topics, image | |
# ์ผ๊ธฐ ์ฃผ์ ์ถ์ฒ ํจ์ | |
def get_initial_response(style, sentence): | |
style = options.get(int(style.split('.')[0]), "๐ผ ์น๊ทผํ") | |
system_prompt_momentum = ( | |
f"๋๋ {style}์ ์ฑ๋ด์ด์ผ. ์ฌ์ฉ์๊ฐ ์์ฑํ ์ผ๊ธฐ๋ฅผ ๋ฐํ์ผ๋ก ์๊ฐ์ ์ ๋ฆฌํ๊ณ ๋ด๋ฉด์ ๋์๋ณผ ์ ์๋๋ก " | |
"๋์์ฃผ๋ ๊ตฌ์ฒด์ ์ธ ์ผ๊ธฐ ์ฝํ ์ธ ๋ ์ง๋ฌธ 4-5๊ฐ๋ฅผ ์ถ์ฒํด์ค." | |
) | |
try: | |
response = openai.ChatCompletion.create( | |
model="gpt-4-turbo", | |
messages=[ | |
{"role": "system", "content": system_prompt_momentum}, | |
{"role": "user", "content": sentence} | |
], | |
temperature=1 | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
return f"๐ ์ฃผ์ ์ถ์ฒ ์ค๋ฅ: {e}" | |
# Gradio ์ธํฐํ์ด์ค | |
with gr.Blocks() as app: | |
gr.Markdown(""" | |
# ๐ EmoDiary : ์ค๋งํธ ๊ฐ์ ์ผ๊ธฐ ๋์ฐ๋ฏธ ๐ | |
**์ค๋์ ํ๋ฃจ๋ฅผ ๊ธฐ๋กํ๋ฉด, ๊ทธ์ ๋ง๋ ํ๋ ์ด๋ฆฌ์คํธ์ ์ผ๊ธฐ ํ๊ณ ์ฝํ ์ธ ๋ฅผ ์๋์ผ๋ก ์์ฑํด๋๋ฆฝ๋๋ค!** | |
### ์ฌ์ฉ ๋ฐฉ๋ฒ: | |
1. ์ค๋์ ํ๋ฃจ ๊ธฐ๋กํ๊ธฐ: ํ๋ฃจ ๋์์ ๊ฐ์ ์ด๋ ์ผ์ด๋ ์ผ์ ํ ์คํธ ๋ฐ์ค์ ๊ธฐ๋กํ๊ณ , ์์ ์ ๊ฐ์ ์ ๋ฐ์ํ ์ ์๋ ์ผ๊ตด ํ์ ์ด๋ฏธ์ง๋ฅผ ์ดฌ์ํด์ฃผ์ธ์. | |
2. AI ์คํ์ผ ์ ํํ๊ธฐ: ์์ ์ ์ ํธ์ ๋ง๋ ์คํ์ผ์ ์ ํํ๋ฉด, ํด๋น ์คํ์ผ์ ๋ง์ถ ๋ต์ฅ๊ณผ ํ๊ณ ์ฃผ์ ๋ฅผ ์ถ์ฒํด๋๋ฆด๊ฒ์. | |
3. ํ๋ ์ด๋ฆฌ์คํธ ์ ํํ๊ธฐ: ์ค๋์ ๊ฐ์ ์ ๋ง์ถฐ "๋น์ทํ" ๋๋ "์๋ฐ๋" ๊ฐ์ ์ ์ ํํ๋ฉด, ๊ทธ์ ๋ง๋ ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ถ์ฒํด๋๋ฆด๊ฒ์. | |
4. ๋ถ์ ์์: "๐ ๋ถ์ ์์" ๋ฒํผ์ ํด๋ฆญํ๋ฉด, ์ ๋ ฅํ ์ ๋ณด์ ์ด๋ฏธ์ง๋ฅผ ๋ฐํ์ผ๋ก ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ, ๊ฐ์ ์บ๋ฆญํฐ ์ด๋ฏธ์ง, ๊ทธ๋ฆฌ๊ณ ํ๊ณ ์ฃผ์ ๊ฐ ์์ฑ๋ฉ๋๋ค.""") | |
with gr.Row(): | |
with gr.Column(): | |
diary_input = gr.Textbox(label="๐ ์ค๋ ํ๋ฃจ ๊ธฐ๋กํ๊ธฐ", placeholder="ex)์ค๋ ์ํ๊ฐ์ ๋ง์๋ ๊ฑธ ๋ง์ด ๋จน์ด์ ์์ฒญ ์ ๋ฌ์ด") | |
chatbot_style = gr.Radio( | |
choices=[f"{k}. {v}" for k, v in options.items()], | |
label="๐ค ์ด๋ค ์คํ์ผ์ AI์๊ฒ ๋ต์ฅ๊ณผ ํ๊ณ ์ฃผ์ ๋ฅผ ์ถ์ฒ๋ฐ๊ณ ์ถ๋์?" | |
) | |
playlist_input = gr.Radio(["๋น์ทํ", "์๋ฐ๋"], label="๐ง ์ค๋์ ๊ฐ์ ๊ณผ ใ ใ ๋๋ ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ถ์ฒ๋ฐ๊ณ ์ถ๋์?") | |
image_input = gr.Image(type="pil", label="๐ท ์ผ๊ตด ํ์ ์ฌ์ง ์ ๋ก๋", width=256, height=256) | |
submit_btn = gr.Button("๐ ๋ถ์ ์์") | |
with gr.Column(): | |
output_playlist = gr.Dataframe(label="๐ง ์ถ์ฒ ํ๋ ์ด๋ฆฌ์คํธ ") | |
output_comment = gr.Textbox(label="๐ฌ AI ์ฝ๋ฉํธ") | |
output_topics = gr.Textbox(label="๐ ์ค๋์ ๋ด๋ฉด์ ๋์๋ณด๋ ํ๊ณ ์ถ์ฒ ์ฃผ์ ") | |
output_image = gr.Image(label="๐ผ๏ธ ์์ฑ๋ ์ค๋์ ๊ฐ์ ์บ๋ฆญํฐ", type="pil", width=256, height=256) | |
# ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ ์ฐ๊ฒฐ | |
submit_btn.click( | |
fn=chatbot_diary_with_image, | |
inputs=[chatbot_style, diary_input, image_input, playlist_input], | |
outputs=[output_playlist, output_comment, output_topics, output_image] | |
) | |
# ์ฑ ์คํ | |
app.launch(debug=True) |