전체 활동내역
- Yesterday
-
1. 임베딩이란 무엇인가요? 임베딩은 자연어 처리(NLP)와 기계학습의 핵심 개념으로, 텍스트 데이터를 인공지능이 이해할 수 있는 숫자 벡터로 변환합니다. 의미적으로 유사한 "멍멍이"와 "강아지" 같은 단어들은 벡터 공간에서도 가까운 위치에 배치됩니다. 임베딩의 가장 큰 특징은 단어나 문장의 의미적 관계를 수학적으로 표현할 수 있다는 점입니다. "왕 - 남자 + 여자 = 여왕"과 같은 벡터 연산이 가능하여 자연어의 의미 관계를 수치적으로 포착합니다. 단순한 단어 수준을 넘어 문장과 문단 전체의 의미까지 포착할 수 있는 임베딩은 검색 시스템, 추천 시스템, 감정 분석 등 다양한 자연어 처리 작업에서 핵심적인 역할을 합니다. 특히 최근 transformer 기반 사전학습 모델의 등장으로 더욱 정교한 임베딩이 가능해졌으며, 이는 기계 번역과 질의응답 시스템 같은 고도화된 AI 서비스의 성능 향상에 크게 기여하고 있습니다. CLOVA Studio의 임베딩 API(v2 포함)를 통해 텍스트 데이터를 1,024차원의 벡터로 변환할 수 있습니다. 이제 화장품 리뷰 데이터를 예시로 활용하여, CLOVA Studio 임베딩v2 API로 텍스트의 의미를 벡터 공간에 표현하고 분석하는 과정을 진행해보겠습니다. 분석 데이터 : review-cosmetic_raw.csv (데이터 출처 : AI Hub 속성기반 감정분석 데이터) 2. 임베딩 시각화를 통한 데이터 분포 확인 리뷰 텍스트 데이터 시각화는 다음 네 단계로 진행됩니다. 화장품 리뷰 텍스트 데이터를 수집하고 전처리합니다. 리뷰의 감정은 긍정(Positive), 중립(Neutral), 부정(Negative)으로 분류되어 있습니다. CLOVA Studio의 임베딩v2 API를 활용하여 각 리뷰 텍스트의 1,024차원 임베딩 벡터를 생성합니다. 고차원의 임베딩 벡터를 t-SNE 알고리즘을 통해 2차원으로 차원 축소합니다. 감정 분류에 따라 색상을 다르게 적용하여 시각화함으로써, 감정별 군집화 패턴을 확인할 수 있습니다. 이러한 과정을 통해 화장품에 대한 고객들의 감정적 반응이 벡터 공간에서 어떻게 분포하는지 파악할 수 있습니다. ① 필요한 모듈 import import time from tqdm import tqdm import pandas as pd from sklearn.manifold import TSNE import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams from sklearn.utils import resample ② 데이터 불러오기 df = pd.read_csv('review-cosmetic_raw.csv', encoding = 'utf-8-sig') # 같은 디렉토리 상의 데이터 불러오기 df.info() # 데이터 정보 확인 print(df.head()) # 데이터 상단 부분 출력 <class 'pandas.core.frame.DataFrame'> RangeIndex: 14835 entries, 0 to 14834 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Index 14835 non-null int64 1 Aspects 14835 non-null object 2 Domain 14835 non-null object 3 GeneralPolarity 14835 non-null int64 4 Text 14835 non-null object dtypes: int64(2), object(3) memory usage: 579.6+ KB Index Aspects Domain \ 0 1 [{'Aspect': '기능/효과', 'SentimentText': ' 거품이 풍부... 화장품 1 2 [{'Aspect': '기능/효과', 'SentimentText': '여드름 피부에... 화장품 2 3 [{'Aspect': '보습력/수분감', 'SentimentText': '유분기도 ... 화장품 3 4 [{'Aspect': '기능/효과', 'SentimentText': '이 제품 쓴다... 화장품 4 5 [{'Aspect': '향', 'SentimentText': '박하향이 처음에는 자... 화장품 GeneralPolarity Text 0 1 거품이 풍부해서 사용하기 편하고, 사용하기 편하고, 세정력도 좋습니다. 피부에 ... 1 0 여드름 피부에 잘 맞는 듯합니다. 좁쌀 여드름이 많이 좋아졌어요. 용량이 작고 사... 2 1 유분기도 여드름도 많이 좋아졌어요. 여드름도 많이 좋아졌어요. 세일할 때 구매하니 ... 3 1 이 제품 쓴다고 여드름이 안 생기는 건 아니지만 자극적이지 않아서 촉촉하지도 않지만... 4 1 박하향이 처음에는 자극적이라 생각했는데 풍부한 거품으로 부드럽게 잘 세정되는것 같아... ③ 임베딩 구하기 본 분석에서는 빠르게 임베딩 값을 구하고 시각화하기 위해 240개의 데이터 포인트 (부정 -1, 중립 0, 긍정 1 : 각 80개씩)를 샘플링하여 사용합니다. def process_batch(batch, executor, max_retries, retry_delay_seconds): results = [] for text in tqdm(batch, desc="Processing Requests"😞 retries = 0 while retries <= max_retries: try: response = executor.execute({"text": text}) if isinstance(response, list😞 results.append(response) break raise ValueError(f"Unexpected response format: {response}") except Exception as e: retries += 1 if retries > max_retries: print(f"Skipping text: {text} due to repeated errors.") results.append(None) else: time.sleep(retry_delay_seconds) return results def main(): executor = CompletionExecutor( host='clovastudio.apigw.ntruss.com', api_key='****', # 올바른 값으로 변경해줘야합니다 api_key_primary_val='***', # 올바른 값으로 변경해줘야합니다 request_id='****' # 올바른 값으로 변경해줘야합니다. ) # 데이터 포인트 240개 샘플링 data = pd.concat([ resample(df[df['GeneralPolarity'] == 1], n_samples=80, random_state=0), resample(df[df['GeneralPolarity'] == 0], n_samples=80, random_state=0), resample(df[df['GeneralPolarity'] == -1], n_samples=80, random_state=0) ]).reset_index(drop=True) batches = [data['Text'][i:i + 60] for i in range(0, len(data), 60)] embeddings = [] for i, batch in enumerate(batches, start=1😞 print(f"Processing batch {i}/{len(batches)}") embeddings.extend(process_batch(batch, executor, max_retries=5, retry_delay_seconds=10)) if i < len(batches): time.sleep(30) # 429에러 발생 방지를 위한 딜레이, 필요시 조정 data['Embedding'] = embeddings print(data) if __name__ == '__main__': main() Processing batch 1/4 Processing Requests: 100%|██████████| 60/60 [00:08<00:00, 7.36it/s] Processing batch 2/4 Processing Requests: 100%|██████████| 60/60 [00:39<00:00, 1.52it/s] Processing batch 3/4 Processing Requests: 100%|██████████| 60/60 [00:44<00:00, 1.35it/s] Processing batch 4/4 Processing Requests: 100%|██████████| 60/60 [00:30<00:00, 1.95it/s] Index Aspects \ 0 3905 [{'Aspect': '자극성', 'SentimentText': '순하고 ', 'S... 1 13891 [{'Aspect': '기능/효과', 'SentimentText': '잡티, 멜라닌... 2 4789 [{'Aspect': '기능/효과', 'SentimentText': '세정력이 뛰어... 3 7049 [{'Aspect': '보습력/수분감', 'SentimentText': '얼굴에 너... 4 13036 [{'Aspect': '제형', 'SentimentText': '제형이 생크림 입자... .. ... ... 235 4639 [{'Aspect': '흡수력', 'SentimentText': '너무 무거운 느낌... 236 5757 [{'Aspect': '편의성/활용성', 'SentimentText': '사용하기 ... 237 9963 [{'Aspect': '기능/효과', 'SentimentText': '주름이 옅어지... 238 5514 [{'Aspect': '피부타입', 'SentimentText': '이 제품은 건성... 239 7696 [{'Aspect': '보습력/수분감', 'SentimentText': '촉촉함이 ... GeneralPolarity Text \ 0 1 순하고 저렴하고 양이 많아요. 촉촉함도 오래가고, 오래가고, 끈적이지 않아요. ... 1 1 잡티, 멜라닌 색소 억제 및 잡티 제거와 커버에도 도움이 되는 것 같아요. 잡티 ... 2 1 세정력이 뛰어난 것 같습니다. 세안하고 나면 뽀득거리면서도 자극적이지 않고 깨끗한 ... 3 1 얼굴에 너무 촉촉하고 ~~ 와~얼굴에 윤기가 광이 나요~~굿 4 1 제형이 생크림 입자 느낌으로 쫀쫀하게 피부에 문지르면 깨끗하게 잘 지워져요. .. ... ... 235 -1 너무 무거운 느낌이라 자주 손이 안가네요. 피부에 팩한 것 처럼 한꺼풀 덮여 있는 ... 236 -1 사용하기 불편합니다. 리프팅 효과도 없는 것 같고 피부에 트러블이 생깁니다. 237 -1 주름이 옅어지는 느낌 안 드네요. 피부는 약간 간질거리고, 붉은 트러블도 생겼어... 238 -1 이 제품은 건성인 분들에게는 뻑뻑하게 발리고 건조한 것 같습니다. 뻑뻑하게 발리고... 239 -1 촉촉함이 덜하고 끈적임이 있어 건조할때 OOO 바르고 건조함이 완화가 되기를 기대했... Embedding 0 [-1.0322266, 1.0615234, -1.0039062, 0.02919006... 1 [-0.70654297, 0.68603516, -1.1757812, -0.28100... 2 [-0.7182617, 1.0566406, -1.0390625, -0.2807617... 3 [0.38183594, 1.3720703, -1.1142578, -0.8857422... 4 [-0.5258789, 0.52441406, -1.1601562, -1.410156... .. ... 235 [0.22509766, 1.0605469, -1.3222656, -0.7373047... 236 [-0.87353516, 1.6015625, -1.5839844, -0.878417... 237 [-1.1142578, 0.8383789, -1.4892578, -0.6748047... 238 [-0.049591064, 1.4492188, -0.671875, -1.814453... 239 [-0.41479492, 1.0751953, -1.3623047, -0.305419... [240 rows x 5 columns] # 임베딩값을 포함한 데이터프레임 저장 data.to_csv('review-cosmetic_emb.csv', index=False, encoding='utf-8-sig') 리뷰데이터에 대해 임베딩 값을 추가하여 저장한 결과 파일은 다음과 같습니다. review-cosmetic_emb.csv matplot, t-SNE 차원 축소를 통해 임베딩 시각화하기 t-SNE(t-Distributed Stochastic Neighbor Embedding)는 고차원 데이터를 저차원으로 변환하여 시각화하는 데 매우 효과적인 비선형 차원 축소 기법입니다. 1024차원의 데이터 포인트의 임베딩값 간의 유사도 관계를 최대한 보존하여 2차원 공간으로 차원 축소하면 데이터 포인트들을 시각화할 수 있습니다. 비슷한 의미를 가진 데이터 포인트들은 2차원 평면상에서 서로 가깝게 위치하게 되어, 임베딩 공간의 구조를 직관적으로 이해할 수 있게 됩니다. 본 분석에서는 빠르게 임베딩 값을 구하고 시각화하기 위해 240개의 데이터 포인트 (부정 -1, 중립 0, 긍정 1 : 각 80개씩)를 샘플링하여 사용합니다. rcParams['font.family'] = 'AppleGothic' plt.rcParams['axes.unicode_minus'] = False embeddings = np.array(data['Embedding'].tolist()) tsne = TSNE(n_components=2, random_state=0, perplexity=30) # 2차원으로 시각화, perplexity는 데이터의 고차원 공간에서 가까운 이웃을 선택하는 방법과 관련이 있으며, 이 값이 크면 더 넓은 범위의 이웃을 고려한다는 의미이고, 작으면 더 국소적인 관계를 이웃으로 고려한다는 의미입니다 # 데이터포인트의 개수가 많을수록 큰 값을 사용하는 것이 일반적이며, 보통 5~50 사이의 값을 선택합니다. tsne_results = tsne.fit_transform(embeddings) data['t-SNE Dimension 1'] = tsne_results[:, 0] data['t-SNE Dimension 2'] = tsne_results[:, 1] polarity_to_color = {1: 'green', 0: 'blue', -1: 'red'} # 1: 긍정, 0: 중립, -1: 부정 colors = data['GeneralPolarity'].map(polarity_to_color) plt.figure(figsize=(10, 10)) plt.scatter( data['t-SNE Dimension 1'], data['t-SNE Dimension 2'], c=colors, alpha=0.5 ) plt.title('t-SNE 시각화: 임베딩 감성 분석') plt.xlabel('t-SNE 차원 1') plt.ylabel('t-SNE 차원 2') plt.legend(handles=[ plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='green', markersize=10, label='긍정 (1)'), plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='blue', markersize=10, label='중립 (0)'), plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='red', markersize=10, label='부정 (-1)') ]) plt.show() 감정 별(긍정, 중립, 부정)로 t-SNE 시각화한 결과입니다. 긍정(1, 초록색)은 주로 하단부에 군집화되었고 부정(-1, 빨간색)은 상단부에 주로 분포한 것으로 확인할 수 있습니다. 또한 중립(0, 파란색)은 중앙에 넓게 분포하여 다른 감정들과 일부 겹치는 영역이 있습니다. 리뷰 텍스트가 감정 별로 구분이 되는 군집으로 형성되어 있고 CLOVA Studio의 임베딩 모델이 감정의 의미적 차이를 잘 포착했다는 것을 확인할 수 있습니다. 일부 영역에서 감정 간 겹칩이 발생하는 것은 리뷰 텍스트의 미묘한 감정표현이나 복잡한 표현이 포함된 경우로 해석할 수 있습니다. plotly, t-SNE 차원 축소를 통해 임베딩 시각화하기: 데이터포인트 내용 확인하기 import plotly.express as px import pandas as pd data['Text'] = data['Text'] df_tsne = pd.DataFrame({ 't-SNE Dimension 1': data['t-SNE Dimension 1'], 't-SNE Dimension 2': data['t-SNE Dimension 2'], 'GeneralPolarity': data['GeneralPolarity'], 'Text': data['Text'] }) df_tsne['GeneralPolarity'] = df_tsne['GeneralPolarity'].astype(str) fig = px.scatter( df_tsne, x='t-SNE Dimension 1', y='t-SNE Dimension 2', color='GeneralPolarity', hover_data=['Text'], color_discrete_map={ "-1": "red", # 부정 "0": "blue", # 중립 "1": "green" # 긍정 }, title='t-SNE 시각화: 감성 분석', width=1200, # 그래프 가로 길이, 필요시 조정 height=800 # 그래프 세로 길이, 필요시 조정 ) fig.update_layout( font=dict( family="AppleGothic", size=16 ), title=dict( font=dict(size=16) ), xaxis_title="t-SNE 차원 1", yaxis_title="t-SNE 차원 2" ) fig.show() plot에 표시된 데이터 포인트의 내용을 인터랙티브하게 바로 확인할 수 있도록 'plotly'를 이용하여 시각화하였습니다. 이를 통해 개별 데이터 포인트에 대한 자세한 정보 확인이 가능하여 실제 리뷰 텍스트의 내용이 어떤지 확인할 수 있습니다. 왼쪽 영역에 위치한 초록색 데이터 포인트를 확인해보니 "제품이 셋 세트라 완전 풍성합니다. 제형이 쫀쫀하게 성분이 콜라겐으로 더 행복한 시간 될 것 같아요"라는 내용의 긍정적인 리뷰 내용을 확인할 수 있습니다. 반면, 오른쪽 영역에 위치한 빨간색 데이터포인트를 확인해보면 "눈시림이 너무 심해서 얼굴에 바를 수가 없네요 자극이 너무 심하네요"라는 내용의 부정적인 리뷰 내용을 확인할 수 있습니다. 3. 임베딩 값을 활용한 분류기 구축과 성능평가 지금까지 우리는 이미 감정 분류가 되어있는, 즉 분류 레이블이 있는 데이터의 임베딩을 구하고 시각화를 해보았습니다. 하지만 실제 대부분의 데이터는 레이블이 없는 데이터를 다루게 되는데요, 이런 상황에서 텍스트 임베딩은 분석에 유용한 도구로 활용될 수 있습니다. 임베딩은 텍스트의 의미적 유사성을 수치화하여 표현된 값이기 때문에, 이를 기반으로 비슷한 의미를 가진 데이터들을 그룹화할 수 있습니다. 유사한 텍스트는 다차원의 임베딩 공간에서 가까운 거리에 위치하게 되기 때문입니다. 이러한 임베딩의 특성을 활용하여, K-means 클러스터링을 통해 비슷한 의미를 가진 텍스트 데이터들을 레이블 없이도 분류가 가능합니다. K-means 클러스터링은 다음과 같은 상황에서 특히 유용합니다. 텍스트로 이루어진 대량의 문서(ex. 고객 cs/리뷰 데이터)를 주제 별로 분류할 때 텍스트 데이터의 의미있는 패턴을 찾고 싶을 때 화장품 리뷰 데이터는 이미 분류 레이블(감정), 즉 분류에 대한 정답을 가지고 있지만 클러스터링 분석의 결과를 실제와 비교해보면서 임베딩 기반 클러스터링의 활용은 어떻게 할 수 있는지 알아보겠습니다. 임베딩 값을 활용한 분류기 구축/성능 평가 과정은 다음과 같습니다. K-means 클러스터링 클러스터링 성능평가하기 ① 필요한 모듈 import import numpy as np import pandas as pd from sklearn.cluster import KMeans from sklearn.feature_extraction.text import TfidfVectorizer import matplotlib.pyplot as plt from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score from scipy.stats import entropy from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from xgboost import XGBClassifier from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay from sklearn.model_selection import GridSearchCV K-Means 클러스터링이란? 텍스트 데이터의 t-SNE 차원 축소 결과를 활용하여 K-Means 클러스터링을 수행하고, 클러스터별 카테고리(ex. 감정) 분포와 주요 키워드를 분석하겠습니다. 이를 통해 데이터의 특성별로 데이터를 나눠서 군집화하고 군집화된 데이터들의 내용으로 데이터의 특징을 파악할 수 있습니다. 코드 설명에 앞서, K-Means 클러스터링 알고리즘에 대해 알아봅시다. K-means는 데이터의 특징을 기반으로 하여 k개의 그룹으로 나누는 비지도 학습 알고리즘입니다. K-means 클러스터링 알고리즘을 도식화 하면 다음과 같습니다. 각 클러스터는 하나의 중심점을 가져야 합니다. 1. 초반에는 데이터 포인트 중 무작위로 k개의 중심점이 정해집니다. 이 초기 중심점들이 각 클러스터의 시작점이 됩니다. 2. 각 데이터 포인트들은 초반에 정해진 시작점을 기준으로 가장 가까운 클러스터에 할당됩니다. 3. 정해진 각 클러스터 내 평균을 계산하여 평균값이 새로운 중심값으로 설정됩니다. 4. 더이상 평균값에 변경이 없이 수렴할 때가지 클러스터를 재할당하는 과정을 거칩니다. ② K-Means 클러스터링 실행하기 앞서 t-SNE로 차원 축소한 데이터를 기준으로 3개의 클러스터를 생성합니다. 각 클러스터에서 부정(-1), 중립(0), 긍정(1) 감정이 얼마나 분포하는 확인해보고, 클러스터 별로 데이터의 특징이 어떻게 나타나는지 확인해보겠습니다. 클러스터링 실행 #1. t-SNE 결과 기반으로 클러스터링 kmeans = KMeans(n_clusters=3, random_state=42) data['Cluster'] = kmeans.fit_predict(data[['t-SNE Dimension 1', 't-SNE Dimension 2']]) # 2. 클러스터별 감성 비율 분석 sentiment_distribution = data.groupby('Cluster')['GeneralPolarity'].value_counts(normalize=True).unstack().fillna(0) print("클러스터별 감성 분포:") print(sentiment_distribution) # 3. 주요 키워드 분석 vectorizer = TfidfVectorizer(max_features=15) # 상위 15개 확인, 필요시 변경 가능 cluster_keywords = {} for cluster_id in data['Cluster'].unique(): cluster_texts = data[data['Cluster'] == cluster_id]['Text'] tfidf_matrix = vectorizer.fit_transform(cluster_texts) keywords = vectorizer.get_feature_names_out() cluster_keywords[cluster_id] = keywords print("클러스터별 주요 키워드:") for cluster_id, keywords in cluster_keywords.items(): print(f"Cluster {cluster_id}: {keywords}") ③ 결과 확인하기 클러스터별 감성 분포: GeneralPolarity -1 0 1 Cluster 0 0.076923 0.646154 0.276923 1 0.707547 0.283019 0.009434 2 0.000000 0.115942 0.884058 클러스터별 주요 키워드: Cluster 2: ['꾸준히' '너무' '만족합니다' '많이' '바르고' '얼굴에' '오래지속되어' '정말''좋네요' '좋습니다' '좋아요' '촉촉하고' '피부가' '피부에' '효과가'] Cluster 0: ['같습니다' '같아요' '느낌이' '들어요' '발림성' '발림성이' '않고' '오래' '좋고' '좋네요' '좋습니다' '좋아요' '향은' '향이' '효과는'] Cluster 1: ['가격대비' '가격도' '같아요' '구성이' '너무' '아쉽네요' '아직' '않네요' '않아요' '용량이' '촉촉하지' '피부가' '효과' '효과가' '효과는'] 클러스터 별 감성 분포 결과를 확인해보니, 각 Cluster 0,1,2마다 부정(-1), 중립(0), 긍정(1) 리뷰 내용이 모여있는 것을 확인할 수 있습니다. Cluster 부정(-1) 중립(0) 긍정(1) 0 7.7% 64.6% 27.7% 1 70.8% 28.3% 0.9% 2 0.0% 11.6% 88.4% Cluster 0는 중립, Cluster 1은 부정, Cluster 2는 긍정적인 리뷰가 다수로 차지하고 있습니다. 각 클러스터의 감정 비율을 통해, 화장품에 대한 리뷰가 부정, 중립, 긍정적으로 잘 나뉘는지를 알 수 있습니다. 클러스터 별 주요 키워드를 확인해보면, 긍정적인 리뷰가 많이 포함된 Cluster 2에서는 "좋아요", "촉촉하고"와 같은 단어가 많이 포함되었습니다. 반면, 부정적인 리뷰가 많이 포함된 Cluster 1에서는 "가격대비", "아쉽네요"와 같은 단어의 빈도가 많습니다. 이러한 결과를 확인해서 긍정적인 클러스터에 자주 언급되는 장점은 이를 강조한 마케팅과 같은 비즈니스 인사이트로 활용하거나 부정적인 클러스터에서 나타난 문제점은 개선점으로 인사이트를 얻을 수 있습니다. ④ 클러스터링 품질 평가하기 클러스터의 품질과 데이터 분포를 다양한 지표를 통해 평가하는 방법에 대해 설명하겠습니다. 먼저 데이터의 클러스터링이 잘 되었는지를 알 수 있는 양적 평가 지표에 대해 알아봅시다. # 지표 함수 정의 def evaluate_clustering(data): # 1. Silhouette silhouette_avg = silhouette_score(data[['t-SNE Dimension 1', 't-SNE Dimension 2']], data['Cluster']) print(f"Silhouette Score: {silhouette_avg}") # 2. Calinski-Harabasz ch_score = calinski_harabasz_score(data[['t-SNE Dimension 1', 't-SNE Dimension 2']], data['Cluster']) print(f"Calinski-Harabasz Score: {ch_score}") # 3. Davies-Bouldin db_score = davies_bouldin_score(data[['t-SNE Dimension 1', 't-SNE Dimension 2']], data['Cluster']) print(f"Davies-Bouldin Score: {db_score}") # 평가 실행 print("### Clustering Quality Evaluation ###") evaluate_clustering(data) ### Clustering Quality Evaluation ### Silhouette Score: 0.3865170180797577 Calinski-Harabasz Score: 262.519196880911 Davies-Bouldin Score: 0.9567987699215011 Silhouette 점수는 0.38로 클러스터링 품질이 양호한 수준입니다. 클러스터 내 데이터 포인트가 서로 밀접하게 모여있으면서 다른 클러스터와 적당히 분리된 상태를 의미하며, 점수가 0.5 이상일 경우 클러스터링 품질이 높다고 평가할 수 있습니다. Calinski-Harabasz 점수와 Davies-Bouldin 점수는 상대적 클러스터링 비교에 사용하기 적절하기 때문에 K값을 적절하게 바꿔가면서 비교하며 더 좋은 클러스터링을 찾는데 적절합니다. 3. 맺음말 1024차원의 임베딩 벡터를 2차원으로 시각화하면 리뷰 텍스트 간의 의미적 관계를 시각적으로 이해할 수 있습니다. 이를 통해 데이터 포인트들이 임베딩 벡터 공간에서 어떻게 분포하는지 파악할 수 있으며, 특정 라벨(긍정, 중립, 부정 등)로 분류된 데이터들이 서로 가깝게 위치하는 경향을 확인할 수 있습니다. 이 과정은 데이터의 라벨링 품질과 임베딩 모델의 성능을 확인하는 데 유용합니다. 예를 들어, 데이터 군집 간의 경계가 명확하지 않다면 임베딩 모델이 텍스트 간의 차이를 충분히 학습하지 못했거나, 라벨링된 데이터에 오류가 있을 가능성을 의미합니다. 이를 통해 오분류된 데이터 포인트를 탐지할 수 있으며, 잘못된 라벨링을 수정하거나 모델 성능을 개선하는 데 활용할 수 있습니다. 또한, K-Means 클러스터링과 XGBoost 모델을 활용하여 데이터를 정량적으로 분석했습니다. K-Means는 임베딩 벡터를 군집화하여 데이터 포인트 간의 유사성을 기준으로 각 클러스터의 특성을 파악하는 데 사용할 수 있습니다. 이 분석 방법은 감정 분석뿐만 아니라 다양한 도메인에서 분류 태스크를 정의하거나 데이터의 라벨링 품질을 확인하는 작업에도 활용될 수 있습니다. 예를 들어, 의료 데이터에서 질병 분류를 하거나 고객 리뷰 데이터를 기반으로 제품 추천을 할 때에도 데이터의 의미적 관계를 확인하고 모델 성능을 개선하는 데 기여할 수 있습니다. 따라서 도메인에 따라 임베딩 모델과 시각화 및 분류 방법을 적절히 조정하여 유용하게 활용할 수 있습니다. 지금까지 우리는 임베딩을 활용한 시각화와, 레이블이 없는 데이터를 가정하고 어떻게 분석하고 군집화할 수 있는지 K-means 클러스터링을 통해 알아봤습니다. 그러나, K-means 클러스터링말고도 더 정교한 분류가 필요할 수 있습니다. 이러한 요구사항을 충족하기 위해, 우리는 XGBoost와 같은 고급 머신러닝 알고리즘을 활용할 수 있습니다. 관련 내용은 "임베딩을 활용한 고급 분류 기법: XGBoost" 문서에서 자세히 다루도록 하겠습니다.
-
플러터 naver Map : 마커를 누르면 다른 infowindow는 닫히고 열려 마커의 infowindow만 열리게 하는 방법
June replied to 코린이's topic in 개발자 포럼: 묻고 답하기
기존 infoWindow를 close() 하면 됩니다. -
예시로 한가지 케이스를 가지고 왔습니다. 위 스샷과 같이 네이버 검색으로는 계룡로 "지하" 97을 검색하게 되면 대전 유성온천역이 정상적으로 검색이 됩니다. 도로명 주소도 보시다시피 계룡로 지하97로 되어 있습니다. (왜 건물번호가 붙어서 나오는지 모르겠지만요?) 하지만, naver.maps.Service.geocode({query: '계룡로 지하 97'}, function(status, response) {}); 위와 같이 유성온천역의 도로명 주소로 질의하면 반환되는 address 정보가 존재하지 않습니다. naver.maps.Service.geocode({query: '계룡로 97'}, function(status, response) {}); 위와 같이 도로명 주소에서 "지하"를 제외하면 유성온천역의 address 정보를 반환 합니다. 지하상가 같은 경우 "지하" 포함과 무관하게 도로명 주소로 geocode 응답 데이터가 없는 케이스들도 다수 확인 되었습니다. naver.maps.Service.geocode({query: '광장로 지하 30'}, function(status, response) {}); naver.maps.Service.geocode({query: '광장로 30'}, function(status, response) {}); 네이버 검색으로도 검색 결과가 없으면 바로 이해를 하겠는데 그게 아니다보니 제가 질의 쿼리를 잘 못쓰는 건지 geocode에서 도로명 주소가 지하인 경우 처리가 잘 안 되는건지 모르겠네요.
-
June joined the community
- Last week
-
쿠크다스 joined the community
-
현재 코딩 시작한지 3주차입니다 먼저 안드로이드 스튜디오환경에서 안드로이드쪽을 진행하고 있고 Client ID, Android 앱 패키지 이름 다 맞게 집어넣었다고 생각합니다. Android 앱 패키지 이름 - com.example.test_sheep clientId : Client ID (X-NCP-APIGW-API-KEY-ID)를 넣어놓은 상태입니다 401에러가 나서 챗봇들한테 물어보니 sha-1키가 문제라고 하는데 넣는곳이 전혀 안보여서 어떻게 해야할지 모르겠습니다. 현재 상황입니다. AndroidManifest.xml package="com.example.test_sheep"> <meta-data android:name="com.naver.maps.map.CLIENT_ID" android:value=""/> com\example\test_sheep\MainActivity.kt package com.example.test_sheep 명시되어 있고 build.gradle.kts defaultConfig { applicationId = "com.example.test_sheep" 또한 제대로 들어가 있으며 main.dart에서도 또한 제대로 명시했다고 생각합니다. void main() async { WidgetsFlutterBinding.ensureInitialized(); try { await NaverMapSdk.instance.initialize( clientId: '', onAuthFailed: (error) { print('네이버맵 인증 실패!!! : $error'); } ); print('네이버맵 초기화 성공'); } catch (e) { print('네이버맵 초기화 중 예외 발생: $e'); } runApp(const SheepDiaryApp()); }
-
나한테왜이래정말슬퍼이렇게계속되면나는개발자가될수없 joined the community
-
sojinkwon joined the community
-
alsdn5531 joined the community
-
flutter로 naver map을 사용 중인데 for문 안에서 NMarker에 id와 position값을 줘서 markers라는 List를 만들었거든요. 여기서 각각의 marker에 고유한 infoWindow 넣는 법과 각각의 marker에서 marker.setOnTapListener 구현 하는 법 알려주세요. ----------------------------------------------------------- static final List<NMarker> markers=[ ]; void initstate() { for(int i=0; i<parkingList.length; i++) { markers.add(NMarker( id:parkingList[i]['name'], position: NLatLng(parkingList[i]['lat'], parkingList[i]['long'])))) } // for문 super.initstate(); } // initstate
-
안녕하세요, @박태호님, 비전 모델을 활용하면 제품의 형태를 파악할 수는 있지만, LLM 특성상 최신 정보나 특정 제품에 대한 전문성은 보장되지 않기 때문에, 단독 구성보다는 이미지 임베딩 등과 결합한 방식으로 구현을 고려하시는 것이 좋습니다. 모델 요금은 아래 링크에서 확인하실 수 있습니다. 비전 모델을 사용하시는 경우에는 HCX-005 모델을 참고해 주세요. 이용 요금 : https://www.ncloud.com/product/aiService/clovaStudio#pricing 클로바 스튜디오 이용을 참고하실 수 있는 가이드 링크를 함께 전달드립니다. 사용 가이드 : https://guide.ncloud-docs.com/docs/clovastudio-overview API 가이드 : https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary 감사합니다.
-
var map = null; map = new naver.maps.Map('map', { gl: true, center: new naver.maps.LatLng(37.5666103, 126.9783882), zoom: 15, // Style Editor에서 발행한 My Style ID를 입력합니다. customStyleId: 'fcb4de4a-487e-4139-9d5c-0d8ccf1d6435' }); 예제에 나와있는 코드대로 적용 하였는데 다크테마로 조차 바뀌지 않습니다. 무슨 문제가 있는걸까요?
-
zest joined the community
-
sunkist joined the community
-
goni joined the community
- Earlier
-
monomart joined the community
-
어떤 주소를 검색하더라도 addresses는 무조건 한 개만 오는데, 제가 뭔가 설정을 잘못한건가요? 문서에 나와있는대로 page, count 등등 설정을 다 해보고, query에는 시, 도, 구, 동 어떤 주소를 입력해도 응답이 무조건 한개만 반환되고 있습니다.
-
jay1 joined the community
-
안녕하세요. 모바일 네이버 지도에서 확인 가능한 과거 도착 시간표를 이용하고 싶은데, 해당 데이터를 다운로드 받을 수 있는지나 API를 통해서 수집이 가능한지 궁금합니다.
-
이용문의 드립니다. 해당 플랫폼에 대한 이해도가 낮아서 가능여부 여쭤봅니다 어떠한 명품 가방을 사진을 플랫폼에 전송하게 되면 해당 제품의 정확한 제품명을 알려주는 시스템도 가능한가요? 이러한 데이터를 계속 학습하여 DB를 만들어가는 과정까지도 가능한지 궁금합니다. 이러한 시스템을 할 경우 어떤 모델로 해야하며 요금은 어떻게 되는지 궁금합니다.
-
안녕하세요, @intube님, 클로바 스튜디오가 비전 모델 및 신규 기능 업데이트 되어 안내드립니다. CLOVA Studio 업데이트 내용 보기 많은 이용 부탁드립니다. 감사합니다.
-
안녕하세요, 네이버 클라우드 플랫폼입니다. AI 기반의 특화된 서비스를 손쉽게 만들 수 있는 개발 도구, CLOVA Studio의 새로운 모델을 소개합니다. 이미지를 이해하는 HCX-005 비전 모델을 제공합니다 ✨ HCX-005 비전 모델의 도입으로 서비스 활용 가능성이 크게 확장되었습니다. 텍스트, 이미지, 표 등 다양한 형태의 데이터를 처리하고 이해할 수 있게 되어, 서비스 영역을 크게 확장하고 더욱 지능적이고 유용한 서비스 제공이 가능해졌습니다. 더욱 강력하고 효율적인 HCX-DASH-002 모델을 만나보세요 ⚡️ 다양한 영역에서 성능이 강화된 HCX-DASH-002 모델은, 비즈니스 로직에 최적화된 모델을 더욱 유연하게 선택할 수 있도록 지원합니다. 긴 길이의 문서도 활용 가능합니다 📚 HCX-005 모델은 128k, HCX-DASH-002 모델은 32k로 context length가 확장되어, 더 긴 문서를 활용할 수 있습니다. (모델 가이드 보기)
-
flutter 로 naver map을 사용하려고 하는데 NaverMap()안에 onMapCreated가 없어요
guitarhero replied to 코린이's topic in 개발자 포럼: 묻고 답하기
onMapReady 여기서 해주시면 되요 -
onMapReady 에서 NMarker로 위치를 지정한 다음에 final naverMarker = NMarker( id: marker.title ?? '', position: NLatLng( marker.point.latitude, marker.point.longitude, ), ); controller.addOverlay(naverMarker); 이런식으로 오버레이 해주면 고정됩니다.
-
재현 가능하도록 Pure JavaScript 코드 알려주시면 디버깅 해보겠습니다. 감사합니다.
-
몇 번이고 확인을 해 봤는데 panToBounds를 두 번 호출하지 않습니다. 또한 말씀하신대로 일부러 두 번 호출하게 코드를 수정해서 체크를 해봐도 특정 위치인((126.915987105,37.483809001),(126.934204909,37.490276924)) 저희 서비스에선 신림동 쪽에서만 idle 이벤트가 발생하지 않습니다. (즉, 해당 지역의 동일한 목표값으로 panToBounds를 두 번 중복 호출해도 다른 곳은 모두 idle 이벤트가 발생합니다.) idle 이벤트가 발생하는, 제가 알지 못하는 또 다른 조건이 있을런지요? 딱히 없다면 사실 현재의 내용만으로는 디버깅이 쉽지는 않을 것이라는 판단입니다. 그래도 친절한 답변 주셔서 많은 도움 되었습니다. 감사합니다.
-
비전 모델을 활용한 비정형 업무 자동화 “이건 사람이 해야만 했잖아요?” 지난 10년간 기업 환경에서는 RPA와 문서 자동화 도구를 통해 정형화된 업무 프로세스의 자동화가 상당한 성과를 거두었습니다. 정해진 화면을 클릭하고, 엑셀에서 데이터를 복사하고, 반복되는 문서 양식을 채우는 일은 이제 대부분 자동화됐습니다. 그러나 주목할 점은 여전히 많은 업무 환경에서 직원들이 이미지, 문서, 그래프 등을 직접 검토하고 분석하여 보고서를 작성하는 데 상당한 시간을 소비하고 있다는 것입니다. 이러한 현상은 주로 '비정형 정보'라는 장벽 때문입니다. 복잡한 문서 구조, 다양한 형태의 시각 자료, 손글씨 메모, 다국어 혼합 콘텐츠 등은 전통적인 자동화 도구로는 처리하기 어려워 여전히 인간의 개입이 필수적이었습니다. 해당 업무들은 자동화의 마지막 난제로 남아 있었습니다. 비전 모델(Vision-Language Model)은 바로 이 지점에서 중요한 돌파구가 될 것입니다. HCX-005와 같은 모델은 이미지와 텍스트를 동시에 이해하며, 인간 수준의 문서 인식, 차트 해석, UI 분석, 손글씨 판별 능력을 갖추고 있습니다. 비전 모델의 도입으로 기존에 자동화되지 못했던 영역에서도 혁신적인 변화가 가능해졌습니다. 이러한 작업들은 기존에는 "사람이 직접 해야 한다"고 여겨졌던 분야들이지만, 비전 모델의 적용으로 효율적인 자동화가 실현됩니다. 사례 검토를 통해 많은 이들이 "이 업무도 자동화가 가능하구나!"라는 새로운 관점을 발견하게 될 것입니다. 본문에서는 익숙한 일상 업무가 혁신적으로 처리되는 실제 사례들을 함께 살펴보며, 자동화의 새로운 가능성을 탐구해보겠습니다. 1. RPA의 새로운 파트너, 비전 모델 RPA(Robotic Process Automation)는 기업 업무 자동화의 강력한 기반이며, 정형화된 입력과 고정된 UI 흐름, 반복 가능한 규칙에서는 여전히 가장 효과적입니다. 하지만 실제 현업의 복잡한 업무는 RPA만으로 처리하기에는 한계가 있습니다. 예를 들어, 양식이 조금만 변경되어도 RPA 시스템이 작동하지 않거나, 스캔 문서나 이미지에서 정보를 추출하는 데 어려움을 겪으며, UI 화면이 업데이트될 때마다 수동으로 재설계해야 합니다. 이러한 문제로 인해 RPA 시스템은 안정적일지라도 예외 상황에서 효율성이 떨어집니다. 여기서 비전 모델이 중요한 역할을 할 수 있습니다. 비전 모델은 RPA가 처리하기 어려운 비정형 정보 해석, 문맥 이해, 예외 상황 대응 등을 담당하여 자동화 프로세스를 더욱 유연하고 실제 업무 환경에 적합하게 만듭니다. ▼ RPA는 정형 업무의 속도를, VLM은 비정형 데이터의 유연성을 제공하며, 두 기술의 결합으로 완전한 자동화 워크플로우가 구축됩니다. ▼ 아래는 '다양한 형식의 인보이스 자동화'의 구체적 사례입니다. 기업에서 자주 다루는 문서들의 형식이 조금씩 다르거나, 다양한 레이아웃을 가진 경우에도 비전 모델이 정확하게 정보를 추출할 수 있는지 테스트해보았습니다. 2. 복잡한 회의 메모 자동 정리 회의 후의 번거로운 과정들 회의 후 화이트보드를 가득 채운 복잡한 그림과 메모. 사진으로는 찍었지만, 막상 다시 보면 어디서부터 정리해야 할지 막막해집니다. 손글씨 메모, 복잡한 도식, 흩어진 키워드들은 일일이 사람이 해석하고 정리해야 했습니다. 회의 자료는 비정형적이고, 흐름을 파악하기 어렵습니다. 기존의 자동화 기술은 화이트보드에 적힌 글자를 단순히 텍스트로 변환할 뿐, 그 안의 맥락을 이해하지 못했습니다. 하지만 이제는 비전 모델을 통해 이러한 비정형적인 회의 이미지 자료를 단순히 ‘읽어내는 것’이 아니라, ‘이해하고 판단해 정리하는 것’까지 가능합니다. 이제 비전 모델은 회의 이미지의 시각적 요소를 분석하고 자연스러운 문서와 행동을 계획하는 전 과정을 지원합니다. 이러한 과정을 자세히 살펴보겠습니다. 화이트보드에 남은 도식과 키워드를 사람이 해석해 작성하던 회의록은 비전 모델을 통해 자동으로 구성되어, 사람의 개입 없이도 완성도 높은 회의록 확보가 가능합니다. 또는 회의 메모에서 액션 아이템을 수동으로 정리하던 과정을 자동화하여, 회의 직후 바로 협업 도구와 연동 가능한 할 일 리스트를 확보할 수 있을 것입니다. ▼ 아래는 회의 중 작성된 복잡한 화이트보드의 예시 입니다. 회의가 끝난 후, 복잡한 화이트보드와 손글씨 메모는 더 이상 정리의 부담이 아닙니다. 대신, 비전 모델이 이러한 시각 자료에서 구조, 흐름 및 맥락을 지능적으로 인식하여 핵심 정보를 체계적으로 정리합니다. 이로써 팀원들은 반복적인 정리 작업에서 벗어나 보다 중요한 논의와 의사결정에 집중할 수 있는 환경을 마련하게 됩니다. 3. 발표 대본 자동 생성하기 슬라이드 작성을 마쳤지만, 발표할 내용을 어떻게 구성해야 할지 막막해지는 순간이 있습니다. 이미지와 도표, 키워드 등을 잘 정리했음에도 불구하고, 발표를 앞두고 나면 어디서부터 시작해야 할지, 어떤 말투로 전달해야 할지에 대한 고민이 깊어지기 마련입니다. 결국 발표 전날 밤, 슬라이드를 한 장씩 넘기며 문장을 하나씩 구상하게 되죠. "이 부분은 이렇게 표현할까?", "여기는 좀 더 설득력 있게 말해야 하는데...", "이 슬라이드는 별도의 설명 없이 넘어가도 괜찮을까?" 슬라이드는 일정한 형식을 따르지만, 발표 대본 작성은 매우 비정형적인 작업입니다. 정해진 형식이나 정답이 없으며, 각 문장은 적절한 톤과 맥락을 고려하여 작성되어야 합니다. 지금까지의 기술로는 이러한 비정형적인 영역에 효과적으로 접근하기 어려웠습니다. 기존 기술은 이미지를 인식할 수 있었지만, 슬라이드가 왜 필요한지, 어떤 내용을 강조해야 하는지, 그리고 청중에게 어떤 흐름으로 설명해야 하는지까지 판단하는 데에는 한계가 있었습니다. 이런 문제를 해결하기 위해 비전 모델을 활용할 수 있습니다. 슬라이드 내용을 가지고 자동으로 발표 대본을 생성하는 기술이 되는거죠. ▼ 발표자료 화면(예: PPT)을 입력으로 받아 비전 모델이 시각적 요소를 분석하고, 언어 모델이 자연어로 대본을 완성하는 전 과정을 보여줍니다. ▼ 아래는 슬라이드(제목, 본문, 그래프 포함)를 비전 모델에 입력하여 자동 발표 대본 생성 성능을 테스트했습니다. 비전 모델의 성능은 매우 인상적이었습니다. 슬라이드의 주제와 배경 맥락을 정확히 이해하고, 본문 핵심 내용을 발표에 적합한 언어로 재구성했을 뿐 아니라, 그래프 요소를 인식하여 자연스럽게 포함시키고 다음 슬라이드와의 연결성까지 고려해 전체 흐름을 매끄럽게 만들었습니다. 이제 대본을 처음부터 작성할 필요 없이 하이퍼클로바X가 제공하는 기본 대본을 활용하여 톤과 길이를 쉽게 조정할 수 있습니다. 백지 상태에서 시작하는 부담을 덜고, 대본 작성부터 예상 질문 준비, 톤 조절까지 효율적인 프레젠테이션 준비가 가능해졌습니다. 4. 외국어 문서의 텍스트 추출/번역/요약을 한 번에 해외 출장 중 현지 파트너로부터 받은 외국어로 된 계약서 부록을 급히 검토해야 하는 상황을 상상해보세요. 수십 페이지에 이르는 법률 용어와 기술 명세로 가득 찬 문서를 마감 시간 내에 이해하고 중요한 변경 사항을 파악해야 합니다. 과거에는 이러한 상황에서 여러 단계의 작업이 필요했습니다. 문서를 스캔하거나 사진으로 찍은 뒤, 텍스트 추출 도구를 사용하고, 번역 프로그램을 통해 번역한 다음, 중요한 내용을 직접 찾아 요약해야 했습니다. 이러한 '문서 획득 → 텍스트 추출 → 번역 → 내용 분석'의 복잡한 과정은 많은 시간을 소모할 뿐만 아니라, 각 단계마다 정보의 정확성이 떨어질 위험이 있었습니다. 그러나 이제 비전 모델 덕분에 이 과정이 획기적으로 간소화되었습니다. 계약서 이미지를 업로드 하면 HCX-005 모델이 텍스트를 정확하게 인식하고, 맥락을 고려하여 번역하며, 중요한 조항과 변경 사항을 효과적으로 요약해 한 번에 제공할 수 있을 것입니다. 시간에 쫓기는 상황에서도 언어 장벽 없이 복잡한 문서의 핵심을 신속하고 정확하게 파악할 수 있게 되었습니다. 숙소 안내문 이미지와 함께 "내용을 정리해줘"라고 요청하면 비전 모델이 정보를 인식하여 체계적으로 구조화된 요약을 제공합니다. 이 기술은 여행 중 마주치는 다양한 공지사항, 음식점 메뉴판, 공공시설 안내문 등을 실시간으로 해석하여 언어 장벽을 효과적으로 해소합니다. 이를 통해 사용자는 낯선 환경에서도 핵심 정보를 신속하게 파악하고 더 나은 여행 경험을 누릴 수 있습니다. 5. 복잡한 표/차트/그래프 분석은 더이상 그만! 이동평균선, 저항선, 추세선, 거래량… 주가 차트를 이해하는 일이 어렵게 느껴지신 적 있으신가요? 각 용어의 정의는 익혔지만, 차트가 전달하는 전체 흐름과 의미를 읽어내는 것은 또 다른 과제였습니다. 특히 여러 보조지표가 한 화면에 겹쳐져 있을 때, 시장이 보내는 신호를 정확히 읽고 해석하는 일은 쉽지 않았을 것입니다. 이제는 비전 모델을 통해 이미지 기반 주식 차트에서도 유의미한 인사이트를 얻을 수 있습니다. 단순한 숫자의 나열을 넘어, 시장 흐름의 핵심 메시지와 변곡점, 그리고 기술적 분석에 기반한 해석까지 도출할 수 있습니다. 이 기술은 주가 차트 분석을 비롯해 논문 내 실험 그래프 자동 요약, 매출/비용 차트의 자동 분석 및 이상 탐지, 그리고 회사 KPI 기반의 대시보드 자동 리포트 생성 등 여러 분야로 확장 가능합니다. 비전 모델을 활용한 분석은 단순한 데이터 인식을 넘어, 다양한 산업에서 의미 있는 의사결정을 자동으로 지원하는 인사이트 도구로 자리잡을 수 있을 것입니다. 6. 학생의 손글씨 시험지 채점, 이제는 비전모델로 학생보다 교사가 더 오래 붙잡고 있어야 했던 시험지, 이제는 AI가 채점합니다 시험이 끝나면 학생들은 자유를 얻지만, 교사에게는 또 다른 업무가 시작됩니다. 특히 수학이나 과학 과목처럼 풀이 과정이 중요한 시험지의 경우, 단순 정답 확인을 넘어 학생의 사고 흐름을 면밀히 검토해야 합니다. 이 과정에서 다양한 글씨체와 표기법 해석, 장시간 집중력 유지의 어려움, 그리고 휴먼 에러로 인한 채점 오류 가능성 등 여러 과제에 직면하게 됩니다. 비전 모델이 이러한 문제들에 실질적인 해결책을 제공합니다. 손글씨 시험지를 이미지로 업로드하면, 비전 언어 모델이 학생의 손글씨를 정확히 인식하고, 대규모 언어 모델이 모범 답안과 비교 분석하여 자동 채점을 완료합니다. 이러한 시스템은 교사의 시간을 절약할 뿐만 아니라, 일관된 평가 기준을 적용해 채점의 공정성과 정확성을 높여줍니다. 이는 교육 현장에서 실질적인 문제를 해결하는 AI 기술의 대표적인 적용 사례가 될 수 있을 것입니다. 시험지 이미지와 정답 이미지를 함께 입력하고 "채점해줘"라고 요청하면 복잡한 개발 과정 없이도 깔끔하게 채점된 결과를 즉시 확인할 수 있어 교육 현장의 실용성이 높습니다. 이 채점 자동화 기술은 칠판 필기 자동 요약, 스마트 학원 시스템, 교사용 학생 관리 리포트 생성, 손글씨 메모 정리 등 다양한 교육 분야로 확장 가능합니다. 7. 비전 모델이 열어가는 제품 마케팅의 새로운 가능성 언어 모델은 마케팅 문구 생성에 혁신적인 변화를 가져왔지만, 제품을 정확히 묘사하기 위해서는 여전히 상세한 설명이 필요했습니다. 제품의 특성, 디자인, 색상, 질감 등을 글로 표현해야만 적절한 마케팅 문구를 얻을 수 있었습니다. 이 과정은 시간이 많이 소요되고 때로는 핵심 특징을 놓치기도 했습니다. 그러나 이제 비전 모델의 등장으로 이러한 한계를 극복할 수 있게 되었습니다. 제품 사진 하나만 업로드하면 비전 모델이 시각적 요소를 분석하고, 제품의 특징을 정확히 파악하여 매력적인 마케팅 문구를 자동으로 생성합니다. 이미지에서 직접 정보를 추출하는 능력 덕분에 마케팅 작업 흐름이 획기적으로 간소화될 것입니다. 이 기술은 단순한 시간 절약을 넘어 제품의 시각적 특성 색상의 미묘한 차이 디자인의 특별한 요소 사용 환경 등을 기반으로 더욱 정확하고 매력적인 카피를 생성합니다. 이 외에도 썸네일 기반 쇼츠 제목 추천, 이메일 제목과 푸시 메시지 자동화, 릴스 스크립트 자동 생성 등으로 다양하게 활용할 수 있어 단시간에 저비용으로 고품질 마케팅 컨텐츠를 생성할 수 있습니다. 마치며 과거에는 불가능하거나 여러 단계를 거쳐야 했던 일들이 많았습니다. 복잡한 문서 구조, 이미지 속 정보, 손글씨, 차트와 같은 비정형 데이터는 자동화의 사각지대에 머물러 있었습니다. 그러나 이제 HCX-005를 통해 이러한 한계가 점차 사라질 것으로 기대됩니다. 더 단순화된 방식으로, 더 빠르고 정확하게, 그리고 무엇보다 현실적인 비용과 품질로 이전에는 시도조차 어려웠던 작업들이 가능해졌습니다. 비전 모델의 등장으로 업무 자동화는 단순 반복 작업을 넘어 이해, 판단, 생성이 필요한 고차원적 영역까지 확장되었습니다. 이는 단순히 기존 기능을 대체하는 도구가 아니라, 산업 전반의 업무 방식을 근본적으로 재정의하고 새로운 가치 창출의 토대가 됩니다. 교육, 마케팅, 행정, 제조, 금융 등 비정형 데이터가 존재하는 다양한 분야에서 이러한 기술의 활용 가능성이 점차 현실화되고 있습니다. 복잡했던 일은 이제 더 이상 복잡하지 않으며, 어렵던 자동화는 누구나 활용할 수 있는 기술이 되었습니다. 비전 모델, HCX-005와 함께 여러분의 비즈니스를 마음껏 성장시켜보세요.
-
panToBounds 를 중복으로 호출했는지 확인 부탁드립니다. 아래처럼 테스트했을 때 정상적으로 `idle` 이벤트 호출됩니다. 다만 중복으로 호출 시 목표 center, zoom 이 똑같기 때문에 지도 이동이 발생하지 않아 `idle` 이벤트를 발생시키지 않습니다. // panToBounds 콘솔창에서 호출 map.panToBounds([126.915987105, 37.483809001, 126.934204909, 37.490276924]); ```js var map = new naver.maps.Map('map', { center: new naver.maps.LatLng(37.3595704, 127.105399), zoom: 10 }); map.addListener('idle', function() { console.log('fire idle!'); }); ```
-
flutter 로 naver map을 사용하려고 하는데 NaverMap()안에 onMapCreated가 없어요.
-
들어가며 본 쿡북에서는 CLOVA Studio의 Chat Completions v3 API를 활용해, 외부 데이터를 조회하거나 도구를 실행하고 그 결과를 반영해 응답하는 AI 어시스턴트를 구축하는 방법을 소개합니다. Chat Completions v3 API 가이드는 다음 링크를 참고해 주세요. (링크) 작동 원리 본 쿡북에서는 금융 어시스턴트를 제작하는 과정을 예제로 다룹니다. 먼저, 어시스턴트의 작동 원리를 살펴보겠습니다. 사용자가 입력한 내용은 messages와 사전에 정의된 tools(functions) 정보가 함께 Function calling 모델에 전달되어 검토됩니다. Function calling 모델은 적합한 function을 선택하고 arguments 값을 응답하는데, 이 과정에서 사용자 발화가 정의된 tools와 무관하다고 판단이 되면 고정 응답이 반환됩니다. 생성된 Function calling 응답은 사용자가 직접 실행하여 그 결과를 messages에 업데이트한 뒤, 다시 Chat Completions API에 전달합니다. Chat Completions이 호출되면 설정된 시스템 프롬프트와 파라미터 값에 기반하여 LLM이 최종 답변을 생성합니다. 사용 시나리오 다음으로, 금융 어시스턴트의 사용 시나리오를 살펴보겠습니다. 시나리오는 아래와 같이 1) 특정 종목의 주가 정보 2) 주식 주문 3) 환율 계산기 도구와 4) 고정 응답으로 구성됩니다. 다음 가이드를 따라 금융 어시스턴트를 직접 만들어 보고, 자신만의 AI 어시스턴트로 확장해 보세요! 🚀 환경 설정 필요한 라이브러리 설치 및 임포트 FinanceDataReader는 국내 및 해외의 금융 데이터(주가, 지수 등)를 불러올 수 있는 파이썬 오픈소스 라이브러리 입니다. 다음 코드를 실행하여 필요한 라이브러리를 설치하고 임포트 하세요. Chat Completions v3 API 요청 정의 Chat Completions v3 API를 통해 함수 호출(Function calling)과 일반 대화 응답 생성을 모두 처리할 수 있습니다. 요청 본문에 도구 정의(tools)를 함께 전달하면, 사용자의 입력에 따라 필요한 도구가 자동으로 선택되고 실행됩니다. 이때 주식 데이터 조회(get_stock_data), 주식 주문(order_stock), 환율 계산(calculate_currency)의 세 가지 도구를 사용할 수 있으며, "toolChoice": "auto"로 설정하면 도구 선택이 자동으로 이뤄집니다. 반대로, 도구를 포함하지 않은 요청은 일반 LLM 응답 생성으로 처리됩니다. 이 경우 temperature, maxTokens 등의 생성 파라미터를 활용해 응답의 스타일과 길이를 조절할 수 있습니다. 성능과 목적에 따라 적합한 모델(HCX-DASH-002, HCX-005)을 선택하여 활용해 주세요. def chat_completions(messages, tools=None, tool_choice="auto", **generation_params): url = "YOUR_API_URL" # 테스트 앱/서비스 앱을 통해 발급 받은 Chat Completions API 호출 경로를 입력하세요. headers = { "Authorization": "Bearer YOUR_API_KEY", # 본인이 발급 받은 API Key를 입력하세요. "X-NCP-CLOVASTUDIO-REQUEST-ID": "YOUR_REQUEST_ID", # 본인이 발급 받은 API의 요청 ID를 입력하세요. "Content-Type": "application/json" } data = { "messages": messages, "toolChoice": tool_choice if tools else "none", **generation_params } if tools: data["tools"] = tools response = requests.post(url, headers=headers, json=data) return response.json() Tools 정의 도구 호출을 활용하려면, API 요청 본문에 사용할 함수 목록을 tools 필드에 정의해야 합니다. 아래는 이 예제에서 사용되는 세 가지 함수 정의입니다. get_stock_data: 특정 종목의 주가나 거래량을 조회합니다. order_stock: 주식을 매수하거나 매도할 수 있습니다. 시장가 또는 지정가 주문을 지원합니다. calculate_currency: 특정 금액을 다른 통화로 환산할 수 있습니다. 각 함수는 이름(name), 설명(description), 입력 파라미터(parameters)로 구성되며, 파라미터는 모두 JSON Schema 형식으로 정의됩니다. 이 정의를 통해 LLM은 어떤 도구가 어떤 인자를 필요로 하는지 이해하고, 사용자 입력에 따라 적절히 호출할 수 있게 됩니다. tools = [ { "type": "function", "function": { "name": "get_stock_data", "description": "특정 종목의 현재 혹은 과거 주가와 거래량을 조회할 수 있습니다.", "parameters": { "type": "object", "properties": { "ticker": { "description": "조회할 종목 코드(ticker)입니다. ex. 네이버의 종목코드는 035420", "type": "string" }, "date": { "description": "조회 날짜입니다. (형식: 'YYYY-MM-DD')", "type": "string" }, "result_type": { "description": "조회 유형입니다.", "type": "string", "enum": ["price", "volume"] } }, "required": ["ticker", "date", "result_type"] } } }, { "type": "function", "function": { "name": "order_stock", "description": "시장가/지정가로 주식을 주문할 수 있습니다.", "parameters": { "type": "object", "properties": { "ticker": { "description": "주문할 종목 코드(ticker)입니다. ex. 네이버의 종목코드는 035420", "type": "string" }, "action_type": { "description": "매수/매도를 지정합니다.", "type": "string", "enum": ["매수", "매도"] }, "shares": { "description": "주문 주식 수를 설정합니다.", "type": "integer" }, "order_type": { "description": "주문 유형을 지정합니다.", "type": "string", "enum": ["시장가", "지정가"] }, "order_price": { "description": "지정가일 경우 가격을 설정합니다.", "type": "number" } }, "required": ["ticker", "action_type", "shares", "order_type"] } } }, { "type": "function", "function": { "name": "calculate_currency", "description": "환율 계산을 도와줍니다.", "parameters": { "type": "object", "properties": { "from_currency": { "description": "환전 대상 화폐 종류를 설정합니다.", "type": "string", "enum": ["KRW", "USD", "EUR", "JPY"] }, "to_currency": { "description": "환전하고자 하는 화폐 종류를 설정합니다.", "type": "string", "enum": ["KRW", "USD", "EUR", "JPY"] }, "amount": { "description": "총 환전 대상 금액입니다. (from_currency 기준)", "type": "number" }, "date": { "description": "환율 기준 날짜입니다. (형식: 'YYYY-MM-DD')", "type": "string" } }, "required": ["from_currency", "to_currency", "amount", "date"] } } } ] 사용자 함수 정의 앞서 정의한 세 가지 도구를 처리하는 사용자 정의 함수를 구현합니다. 사용자 함수는 API 뿐만 아니라 데이터베이스, 로컬 또는 클라우드 저장소의 파일 등 다양한 소스 및 기능과 연동하여 활용될 수 있습니다. get_stock_data 파이썬 오픈소스 라이브러리(FinanceDataReader)를 활용해 주식 데이터를 조회하는 함수를 정의합니다. 종목 코드와 날짜를 기반으로, 해당 시점의 주가 또는 거래량 데이터를 반환합니다. 이외 다양한 금융 데이터 소스와 연동하거나, 데이터 처리 로직을 추가해 보다 심화된 결과를 제공할 수 있습니다. def get_stock_data(ticker, date, result_type): stock_data = fdr.DataReader(ticker, date) if stock_data.empty: return [{"name": "get_stock_data", "response": f"{ticker}에 대한 {date} 데이터가 없습니다."}] row = stock_data.iloc[0] if result_type == "price": return [{"name": "get_stock_data", "response": {"date": date, "price": float(row['Close'])}}] elif result_type == "volume": return [{"name": "get_stock_data", "response": {"date": date, "volume": int(row['Volume'])}}] else: return [{"name": "get_stock_data", "response": "잘못된 조회 유형입니다. '가격' 또는 '거래량'만 조회 가능합니다."}] order_stock 주식 주문을 처리하는 함수를 정의합니다. 종목 코드, 주문 유형(매수, 매도) 등을 바탕으로 처리한 결과를 반환합니다. 현재는 예제로서 단순 메시지를 반환하지만, 실제로는 주문 데이터를 DB에 기록하거나 주문 API를 호출하는 방식으로 확장 가능합니다. def order_stock(ticker, action_type, shares, order_type, order_price=None): if order_type == "지정가" and (order_price is None or order_price <= 0): return [{"name": "order_stock", "response": "지정가 주문에는 유효한 가격이 필요합니다."}] # DB 연동하여 확장 가능 return [{"name": "order_stock", "response": f"{ticker} 종목 {shares}주 {order_type}로 '{action_type}' 주문 완료"}] calculate_currency 환율 계산을 수행하는 함수를 정의합니다. 환전할 금액, 통화 종류 등을 입력 받아 계산 결과를 반환합니다. 현재는 예제로서 하드코딩 된 가상의 고정 환율 데이터를 사용하고 있지만, 실제로는 로컬 파일(json, xls, txt 등) 또는 외부 API를 통해 데이터를 읽어오는 방식으로 확장할 수 있습니다. def calculate_currency(from_currency, to_currency, amount, date): exchange_rates = { "KRW": {"USD": 0.0006725, "EUR": 0.0006178, "JPY": 0.09817}, "USD": {"KRW": 1487.07, "EUR": 1.0884, "JPY": 150.99}, "EUR": {"KRW": 1618.79, "USD": 0.9188, "JPY": 138.74}, "JPY": {"KRW": 10.187, "USD": 0.00662, "EUR": 0.00721}, } rate = exchange_rates.get(from_currency, {}).get(to_currency) if not rate: return [{"name": "calculate_currency", "response": "지원되지 않는 통화입니다."}] converted_amount = round(amount * rate, 2) return [{"name": "calculate_currency", "response": f"{converted_amount} {to_currency}"}] 함수 실행 처리 다음으로 정의한 process_tool_calls는 해당 도구에 정의된 함수와 매개변수를 매핑하여 동적으로 실행해 주는 역할을 합니다. 이를 통해 다양한 도구 호출 시 공통 로직을 단순화할 수 있습니다. def process_tool_calls(tool_name, arguments): tool_map = { "get_stock_data": get_stock_data, "order_stock": order_stock, "calculate_currency": calculate_currency } func = tool_map.get(tool_name) # 추출한 매개변수를 키워드 인수로 함수에 전달하여 실행합니다. return func(**arguments) 실행 및 테스트 사용자 요청을 처리하고 최종 응답을 생성하는 전체 실행 과정을 테스트합니다. 사용자 쿼리를 입력하면 적합한 도구를 선택해 실행한 결과를 AI 대화 흐름에 추가하여 응답을 제공합니다. 사용자 요청이 정의된 도구와 일치하지 않거나, 적합한 도구를 선택할 수 없는 경우 "toolCalls"가 비어 있을 수 있습니다. 다음 코드에서는 이러한 상황을 감지하고 적절한 오류 메시지를 반환하도록 처리합니다. query = "YOUR_QUERY" messages = [{"role": "user", "content": query}] print(f"User: {query}") # Step 1. 요청 — 입력 및 함수 정의 전달 function_response = chat_completions( messages = messages, tools = tools ) # Step 2. 응답 — 호출할 함수 및 인수 추출 tool_calls = function_response.get("result", {}).get("message", {}).get("toolCalls", []) try: if not tool_calls: # 도구 호출이 없을 경우 error_message = "죄송하지만, 입력하신 내용은 제가 처리할 수 있는 범위를 벗어났습니다. 주식 정보나 환율 계산과 관련된 요청으로 다시 시도해 주세요." messages.append({"content": error_message, "role": "assistant"}) print(f"Assistant: {error_message}") else: # 도구 호출이 있을 경우 messages.append({"role": "assistant", "content": "", "toolCalls": tool_calls}) tool_call = tool_calls[0] print(f"\nAssistant: {tool_call}\n") # Step 3. 함수 실행 — 응답 기반으로 실제 함수 호출 tool_call_id = tool_call["id"] tool_name = tool_call["function"]["name"] arguments = tool_call["function"]["arguments"] results = process_tool_calls(tool_name, arguments) messages.append({"role": "tool", "content": str(results), "toolCallId": tool_call_id}) print(f"Tool: {results}\n") # Step 4. 요청 — 함수 실행 결과 전달 chat_response = chat_completions( messages = messages, seed = 0, topP = 0.8, topK = 0, maxTokens = 1024, temperature = 0.5, repeatPenalty = 1.1, stopBefore = [], includeAiFilters = True ) # Step 5. 응답 — 최종 답변 추출 final_answer = chat_response.get("result").get("message").get("content") print(f'Assistant: {final_answer}') except Exception as e: error_message = f"요청 처리 중 오류가 발생했습니다: {str(e)}" messages.append({"content": error_message, "role": "assistant"}) print(f"Assistant: {error_message}") 다음은 위 코드 실행 시 출력 예시입니다. 이 출력은 사용자 요청이 Function calling과 사용자 정의 함수를 거쳐 최종 응답으로 생성되는 단계를 순차적으로 보여줍니다. output sample 1 User: 네이버 그저께 주가 Assistant: {'id': 'call_abA5RsWViXcaCa7FCWg1eoDf', 'type': 'function', 'function': {'name': 'get_stock_data', 'arguments': {'ticker': '035420', 'date': '2025-04-06', 'result_type': 'price'}}} Tool: [{'name': 'get_stock_data', 'response': {'date': '2025-04-06', 'price': np.float64(191800.0)}}] Assistant: 네이버의 그저께 주가는 191,800원이었습니다. output sample 2 User: 엔비디아 10주를 100불에 매도해줘 Assistant: {'id': 'call_Vy7hJQnTicsB1PFa4tscjl0W', 'type': 'function', 'function': {'name': 'order_stock', 'arguments': {'ticker': 'NVDA', 'action_type': '매도', 'shares': 10, 'order_type': '지정가', 'order_price': 100}}} Tool: [{'name': 'order_stock', 'response': "NVDA 종목 10주 지정가로 '매도' 주문 완료"}] Assistant: 사용자님께서 말씀하신 NVDA 종목 10주에 대한 지정가 매도 주문이 완료되었습니다. output sample 3 User: 만원을 달러로 환전하면 얼마인가요 Assistant: {'id': 'call_b7ianfYh9jiEk1csGgWCZKiI', 'type': 'function', 'function': {'name': 'calculate_currency', 'arguments': {'from_currency': 'KRW', 'to_currency': 'USD', 'amount': 10000, 'date': '2025-04-08'}}} Tool: [{'name': 'calculate_currency', 'response': '6.76 USD'}] Assistant: 만 원(10,000 KRW)을 달러로 환전하면 약 6.76 USD입니다. (환율은 2025년 4월 8일 기준입니다.) output sample 4 User: 오늘 날씨 어때? Assistant: 죄송하지만, 입력하신 내용은 제가 처리할 수 있는 범위를 벗어났습니다. 주식 정보나 환율 계산과 관련된 요청으로 다시 시도해 주세요. 실전 활용 팁 이 섹션에서는 Function calling에서 복잡한 사용자 요청을 효과적으로 처리하는 방법을 예시와 함께 알아봅니다. 예를 들어, 특정 종목의 주가를 조회하면서 동시에 주식 주문을 처리하는 복잡한 멀티 쿼리 시나리오도 쉽게 해결할 수 있습니다. 1. 병렬 도구 호출: 여러 도구를 동시에 실행하기 여러 도구를 동시에 호출한 뒤, 그 결과를 종합하여 사용자 요청에 응답할 수 있습니다. 복잡한 요청을 단계별로 실행할 때 유용합니다. User: 지금 테슬라 주가 얼마야? 시장가로 20주 매수 주문해줘. Assistant: [{'id': 'call_eeYdvrLHG69sYFJgC95XBjCd', 'type': 'function', 'function': {'name': 'get_stock_data', 'arguments': {'ticker': 'TSLA', 'date': '2025-04-08', 'result_type': 'price'}}}, {'id': 'call_fsVYRYX5hGI9OvzObPe4gxqG', 'type': 'function', 'function': {'name': 'order_stock', 'arguments': {'ticker': 'TSLA', 'action_type': '매수', 'shares': 20, 'order_type': '시장가'}}}] Tool: [{'name': 'get_stock_data', 'response': {'date': '2025-04-08', 'price': 233.2899932861328}}] Tool: [{'name': 'order_stock', 'response': "TSLA 종목 20주 시장가로 '매수' 주문 완료"}] Assistant: 현재(2025년 4월 8일) 테슬라(TSLA)의 주가는 **233.29** 달러입니다. 또한 요청하신 대로 시장가로 20주를 매수하는 주문을 완료했습니다. 추가적인 도움이 필요하시면 말씀해 주세요! 2. 도구 간 의존성 처리: 특정 도구의 출력 결과를 다른 도구의 입력으로 사용하기 한 도구의 출력이 다른 도구의 입력으로 사용되는 경우, 시스템 프롬프트와 큐(queue)를 활용하여 도구 간 의존성을 처리할 수 있습니다. Function calling 호출 시 시스템 프롬프트에 의존성을 "@도구이름.출력키" 형식으로 명시하게끔 하여 각 도구가 필요한 데이터를 다른 도구의 결과에서 자동으로 참조하도록 설정합니다. 그리고 각 도구 호출은 큐로 관리하며, 의존성이 해결된 도구부터 순차적으로 실행해 결과를 축적하고, 의존성이 충족되지 않은 도구는 다시 큐에 삽입해 처리합니다. 결과적으로 모든 호출이 처리된 후 최종 응답을 생성하여 사용자에게 제공합니다. User: 오늘 테슬라 주가 알려주고 한화로 계산하면 얼마인지 계산해줘 Assistant: {'id': 'call_QqLOLiZYwgqG44mXXNP2ktHL', 'type': 'function', 'function': {'name': 'get_stock_data', 'arguments': {'ticker': 'TSLA', 'date': '2025-04-08', 'result_type': 'price'}}} Tool: {'date': '2025-04-08', 'price': 233.2899932861328} Assistant: {'id': 'call_Gc7gkcZUItMKF3n6sw3p6nw3', 'type': 'function', 'function': {'name': 'calculate_currency', 'arguments': {'from_currency': 'USD', 'to_currency': 'KRW', 'amount': 233.2899932861328, 'date': '2025-04-08'}}} Tool: 346487.31 KRW Assistant: 2025년 4월 08일 기준으로 테슬라(TSLA)의 주가는 **233.29 USD**이며, 이를 한화로 환산하면 약 **346,487원**입니다. 마무리 CLOVA Studio의 Chat Completions v3 API를 활용해, 도구(Function)를 호출하고 자연스러운 대화 응답을 생성하는 방법을 살펴보았습니다. 이번 쿡북을 통해 Function calling의 작동 원리를 이해하고, 이를 활용해 자신만의 데이터 소스와 도구를 결합하여 AI 어시스턴트를 제작해 보시길 바랍니다!
-
Function calling, 쉽게 알아보기 LLM 에이전트가 우리의 모든 질문에 답변할 수 있다면 얼마나 편리할까요? 하지만 LLM은 미리 학습한 데이터를 기반으로 답변하기 때문에 실시간 정보 제공이나 외부 데이터 활용에 한계가 있습니다. 이러한 문제를 해결하기 위해 등장한 기술이 바로 'Function calling'입니다. 이 기능은 LLM이 외부 도구를 활용해 필요한 정보를 가져올 수 있도록 돕는 역할을 합니다. 모델이 스스로 해결할 수 없는 질문이 있을 때, 외부에서 정보를 받아와 더 정확한 답변을 제공할 수 있습니다. 이처럼 LLM이 외부 시스템이나 API를 사용할 수 있기 때문에, Function calling을 '도구(tool)'라고도 부릅니다. Function calling은 스킬(참고: 스킬 트레이너)과 유사해 보일 수 있지만 차이가 있습니다. 스킬은 트레이너 내에 API를 등록해 모델이 스스로 답변까지 생성해 주는 반면, 이 기능은 개발자가 외부 API를 직접 실행해야 합니다. 또한, Function calling은 API뿐만 아니라 스크립트나 라이브러리 내 함수 호출도 가능하다는 점에서 차이가 존재합니다. Function calling이 무엇인지, 그리고 어떻게 활용할 수 있는지 소개해드리겠습니다. Function calling은 어떻게 작동할까요? Function calling은 LLM(대규모 언어 모델)이 사용자의 질문에 맞춰 미리 정의된 함수를 사용해 필요한 기능을 실행하도록 돕는 방식입니다. 예를 들어, 사용자가 ❶"오늘 강남구 날씨는 어때?"라고 질문하면, ❷모델은 이 질문을 전달 받아 분석해 ❸사전에 정의된 함수 중 get_weather를 사용해야 한다고 판단합니다. 이 과정에서 모델은 "강남구"라는 위치 정보와 "2025-04-17"이라는 날짜를 파라미터로 추출합니다. 여기서 중요한 점은, 모델은 함수를 직접 호출하지 않는다는 것 입니다. 대신, 모델은 사용할 함수를 결정하고 필요한 매개변수를 포함한 데이터를 생성합니다. 즉, 사용자의 요청에 따라 미리 정의된 함수 중 어떤 것을 사용하고 어떤 값을 넣어야 할지 결정하는 역할을 합니다. 이후, ❹외부의 날씨 서비스 API에 연결해 ❺실시간 날씨 데이터를 받아오고, ❻그 정보를 모델에게 전달하면 ❼ 모델이 최종답변을 완성합니다. 이를 통해 ❽"현재 강남구의 온도는 18도 이고, 날씨는 흐립니다." 와 같은 응답을 제공할 수 있습니다. Function calling은 실제로 어떻게 사용하나요? 앞서 설명드린 동작 과정을 바탕으로, "사용자 입력 → 모델이 tool 선택 → 외부 함수 결과 전달 → 최종 응답 생성" 이라는 흐름이 코드에서 어떻게 구현되는지 아래 예시를 참고해주세요. Step 1: 사용자 메시지+함수 정의 전송 사용자 입력과 함께 사용할 수 있는 함수 목록을 모델에 전달합니다. 모델은 이를 바탕으로 어떤 도구(tool)를 사용할지 결정합니다. 응답 결과 Step 2: toolCallId에 대한 함수 결과 전달 모델이 사용자 입력에 대해 get_weather 함수를 호출해야 한다고 판단했습니다. 이제 실제 날씨 API를 호출한 결과를 "role": "tool" 메시지로, toolCallId와 함께 전달합니다. 이때는 함수 정의를 다시 포함하지 않아도 되지만, 이후에 사용자로부터 추가 질문(예: "그럼 부산 날씨는 어때?")이 들어올 가능성이 있다면 해당 함수 정의를 다시 포함해 전달하는 것이 좋습니다. Step 3: 최종 응답 생성 모델은 전달받은 함수 결과를 바탕으로 사용자에게 자연스러운 형태의 최종 응답을 생성합니다. Function calling, 이렇게 사용해보세요! 1. 외부 시스템과 연동된 LLM 에이전트 설계하기 Function calling은 LLM이 외부 데이터를 활용하여 더욱 다양한 기능을 제공할 수 있도록 만들어줍니다. 이를 통해 단순한 질의응답을 넘어 고객의 다양한 요구를 충족시키고 반영할 수 있습니다. Shopping LLM 에이전트에서 특정 상품의 재고를 조회하는 방법 Shopping 에이전트를 제작할 때, 여러 가지 함수들을 사전에 정의합니다. 여기에는 재고 조회, 상품 정보 조회, 결제 처리 등 다양한 함수를 포함할 수 있습니다. 예를 들어, 사용자가 특정 상품의 재고를 물어볼 경우엔 getCount 함수를 사용할 수 있습니다. 이 함수는 특정 상품의 ID와 사용자가 위치한 지역 코드를 기반으로 상품의 재고 수량을 확인할 수 있습니다. Function calling은 먼저 사용자의 쿼리(예: “이 상품 재고 있나요?”)에서 getCount 함수를 선택하고 필요한 파라미터(상품 ID, 지역 코드)를 추출합니다. 이후 함수를 실행한 결과값(예.재고:5권)을 모델에게 전달하면 모델이 최종 답변을 생성하여 사용자에게 응답할 수 있습니다. 2. 작업 자동화를 통해 업무 효율성 증진하기 기존의 작업 자동화 방식은 명확한 룰 베이스에 기반한 반복 업무에 특화되어 있었지만, LLM으로 이러한 한계를 극복할 수 있게 되었습니다. 이때 Function calling은 외부 시스템과 연동을 통해 자동화 영역을 확장하고, 더 많은 작업을 처리하도록 도와줍니다. 매일 아침 최신 기사들의 내용을 요약하여 사내 게시판에 업로드하기 뉴스 API를 통해 최신 뉴스를 수집한 후, LLM 모델을 사용하여 각 기사의 핵심 내용을 요약할 수 있습니다. (참고 : 시간이 부족한 모두를 위한 요약 API, 이렇게 활용하세요) 사내 게시판에 업로드하기 위해 uploadnews 함수를 미리 정의하며, 이 함수는 게시글의 제목과 내용을 입력값으로 받습니다. 이후 Function calling을 사용해, LLM이 요약한 내용에서 제목과 본문을 추출하고 이를 uploadnews 함수에 전달합니다. uploadnews 함수가 실행되면 사내 게시판에 요약된 뉴스들이 게시되고, 이를 확인할 수 있습니다. 3. 필요한 정보 추출하기 Function calling은 일반적인 LLM보다 자연어 문서에서 핵심 정보를 추출하는 작업에 더욱 효과적입니다. 1) 명확하게 정해진 형식(스키마)에 맞춰 핵심 데이터 추출 가능 특정 책에 대한 작가명과 출판사를 추출하는 상황을 가정해봅시다. Function calling을 활용하면, 마치 설문지를 작성하 듯 질문에 맞춰 데이터를 정리된 형식으로 반환하여 이를 바탕으로 답변을 생성합니다. 이처럼 데이터를 구조화된 형식을 정의하면, LLM이 자연어 기반의 문서로부터 필요한 정보를 항목별로 추출할 수 있도록 도와줍니다. 2) JSON 형식의 결과 데이터를 별도의 변환 과정 없이 추가 작업 가능 뉴스 정보 추출이 완료되면, 추출된 정보를 저장하는 추가 작업이 필요합니다. 일반적인 LLM의 답변은 자유로운 형태로 제공되지만, 구체적인 형식이나 구조가 부족해 문서화나 저장을 위해 추가 작업이 필요할 때가 있습니다. 이때 Function calling을 사용하면 추출된 데이터를 JSON 형식으로 바로 응답받을 수 있어 별도의 변환 작업이 필요하지 않습니다. 추출된 데이터를 DB에 저장할 때는 정리된 설문지를 그대로 보관하는 것처럼, JSON 형식으로 받은 데이터를 그대로 전달할 수 있습니다. Function calling 성능 업그레이드 비법 1. 자세한 설명과 함께 직관적인 이름 사용하기 함수 및 매개변수의 이름은 쉽게 알아볼 수 있도록 명확하게 지정하는 것이 중요합니다. 약어나 줄임말은 지양하고, 함수가 언제 호출되어야 하는지 설명을 함께 제공하는 것이 좋습니다. 복잡한 함수의 경우, 각 매개변수가 어떤 역할을 수행하는지 설명해주면 도움이 됩니다. 함수 설명은 최소 3~4문장 이상으로 구성하여, 함수의 사용 시점과 제한 사항 그리고 매개변수가 함수의 동작에 미치는 영향을 포함합니다. 2. 명확한 매개변수 지정하기 직관적인 매개변수의 이름과 함께 설명을 제공합니다. 예를 들어, 날짜 형식을 필요로 하는 매개변수의 경우 YYYY-MM-DD 또는 DD/MM/YY와 같은 형식을 명확히 설명하여 사용자가 올바르게 입력할 수 있도록 합니다. 3. 열거형으로 명확한 매개변수 값 제한하기 가능하다면 매개변수에 열거형(enum)을 사용하여 값을 제한하는 것이 좋습니다. 예를 들어, 티셔츠의 사이즈를 선택할 때 S, M, L과 같은 옵션으로 값을 명확하게 제한하면 모델이 더 정확하게 작동할 수 있습니다. 이를 통해 잘못된 값이나 예상치 못한 값이 반환되는 상황을 줄일 수 있습니다. { "name": "pick_tshirt_size", "description": "사용자가 원하는 티셔츠 사이즈를 선택할 때 사용", "parameters": { "type": "object", "properties": { "size": { "type": "string", "enum": ["s", "m", "l"], "description": "사용자가 선택한 티셔츠 사이즈" } }, "required": ["size"], "additionalProperties": false } } 4. 함수의 개수 최적화하기 한 번에 너무 많은 함수를 사용하지 않도록, 10~20개 내외로 유지하는 것이 좋습니다. 그 이상이 되면 모델이 올바른 함수를 선택하는 데 어려움을 겪을 수 있습니다. 필요한 기능이 많다면, 함수들을 논리적으로 그룹화하거나 여러 도구(tool)로 나누어 사용해보세요. 마무리 CLOVA Studio Chat Completions v3 API의 Function calling의 기능을 알아보고 활용법을 알아보았습니다. 외부 데이터를 가져오고 실제 액션을 수행할 수 있는 Function calling을 어떻게 활용할 수 있을지 고민해보세요.🚀