반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

오늘 다룰 예제는 Visualizing the dmbeddings in 2D 입니다.

 

openai-cookbook/Visualizing_embeddings_in_2D.ipynb at main · openai/openai-cookbook · GitHub

 

GitHub - openai/openai-cookbook: Examples and guides for using the OpenAI API

Examples and guides for using the OpenAI API. Contribute to openai/openai-cookbook development by creating an account on GitHub.

github.com

 

여기서는 t-SNE를 사용해서 임베딩의 dimensionality (차원)dmf 1536에서 2로 줄일겁니다. 일단 임베딩이 이렇게 줄어들면 그것을 2D scattter plot (2D 산점도 플롯)을 할 수 있게 됩니다. 

오늘 쓸 데이터도 fine_food_reviews_with_embeddings_1k.csv 입니다.

아래 글에서 이 데이터를 다운 받을 수 있는 방법을 설명했습니다.

IT 기술 따라잡기 :: Openai cookbook - Embeddings - Text comparison examples - Semantic text search using embeddings (tistory.com)

 

Openai cookbook - Embeddings - Text comparison examples - Semantic text search using embeddings

오늘은 openai cookbook 에 있는 Embeddings 부문의 Text comparison examples 에 있는 Semantic_text_search_using_embeddings.ipynb 예제를 살펴 보겠습니다. 우선 이 예제를 살펴 보기 전에 준비해야 할 사항들이 몇가지

coronasdk.tistory.com

이 데이터를 만드는 파이썬 코드는 아래에 있습니다.

 

openai-cookbook/Obtain_dataset.ipynb at 2f5e350bbe66a418184899b0e12f182dbb46a156 · openai/openai-cookbook · GitHub

 

GitHub - openai/openai-cookbook: Examples and guides for using the OpenAI API

Examples and guides for using the OpenAI API. Contribute to openai/openai-cookbook development by creating an account on GitHub.

github.com

 

1. Reduce dimensionality

t-SNE decomposition (분해)를 사용해서 dimensionality를 2차원으로 줄입니다.

 

import pandas as pd
from sklearn.manifold import TSNE
import numpy as np

# Load the embeddings
datafile_path = "data/fine_food_reviews_with_embeddings_1k.csv"
df = pd.read_csv(datafile_path)

# Convert to a list of lists of floats
matrix = np.array(df.embedding.apply(eval).to_list())

# Create a t-SNE model and transform the data
tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)
vis_dims = tsne.fit_transform(matrix)
vis_dims.shape

모듈은 pandas, numpy 그리고 sklearn.manifold의 TSNE를 사용합니다.

모두 이전 글에서 배운 모듈들 입니다.

 

판다스의 read_csv() 함수를 사용해서 csv 데이터 파일을 읽습니다.

 

그 다음 numpy 의 array()를 사용해서 csv 파일의 embedding 컬럼에 있는 값들을 리스트 형식으로 변환합니다.

 

그리고 TSNE()를 사용해서 계산해 줍니다.

이 함수는 고차원 데이터를 시각화 해 주는 것입니다.

sklearn.manifold.TSNE — scikit-learn 1.2.1 documentation

 

sklearn.manifold.TSNE

Examples using sklearn.manifold.TSNE: Comparison of Manifold Learning methods Comparison of Manifold Learning methods Manifold Learning methods on a severed sphere Manifold Learning methods on a se...

scikit-learn.org

fit_transform()은 전달받은 파라미터를 embedded space로 fit 해 주고 transformed output으로 return 합니다.

 

 

그 다음 shape으로 이 데이터의 차원이 어떤지 봅니다.

이것을 실행하면 아래와 같은 값을 얻을 수 있습니다.

 

이 csv 파일의 embedding 컬럼에는 1000개의 행이 있고 그것은 2차원으로 돼 있다는 의미 입니다.

이 작업은 TSNE()에서 2차원으로 줄이는 작업을 한 것입니다.

파라미터 중 n_component=2 라고 돼 있어서 그런 겁니다.

perplexity 함수는 가장 가까운 neighbor들의 수와 관계가 있는 manifold 학습 알고리즘 입니다.

더 큰 데이터세트는는 더 큰 perplexity를 요구합니다. 5에서 50사이를 선택합니다.

random_state는 난수 생성과 관련이 있습니다.

그 다음 init은 initialization을 의미하는 것으로 임베딩을 초기화 하는 겁니다. 이 임베딩을 random 이라고 초기화 합니다.

그리고 learning_rate는 그 숫자가 너무 높을 경우 데이터의 neighbor 와의 거리를 너무 가깝게 설정하게 돼 공처럼 분포할 수 있고 너무 낮으면 빽뺵한 구름처럼 보일 수 있습니다.  

이 외에도 TSNE()에서 사용할 수 있는 다른 파라미터들이 많이 있습니다. 

이곳에서 확인하세요.

sklearn.manifold.TSNE — scikit-learn 1.2.1 documentation

 

sklearn.manifold.TSNE

Examples using sklearn.manifold.TSNE: Comparison of Manifold Learning methods Comparison of Manifold Learning methods Manifold Learning methods on a severed sphere Manifold Learning methods on a se...

scikit-learn.org

하여간 이 TSNE() 함수에서 2차원으로 줄인 겁니다.

 

TSNE() 함수를 이용하기 전의 matrix의 값은 아래와 같습니다.

 

이 것을 TSNE()로 처리를 하면 이렇게 됩니다.

 

이 값을 shape을 이용해서 리스트의 크기와 차원을 표시하면 위에 처럼 1000,2 라고 나옵니다.

 

2. Plotting the embeddings

위에서 처럼 2차원으로 데이터를 정리하면 2D 산점도 분포도를 그릴 수 있다고 했습니다.

아래에서는 그것을 그리기 전에 알아보기 쉽도록 각 review에 대한 색을 지정해서 알아보기 쉽도록 합니다.

이 색은 별점 점수와 ranging 데이터를 기반으로 빨간색에서 녹색에 걸쳐 표현됩니다.

 

import matplotlib.pyplot as plt
import matplotlib
import numpy as np

colors = ["red", "darkorange", "gold", "turquoise", "darkgreen"]
x = [x for x,y in vis_dims]
y = [y for x,y in vis_dims]
color_indices = df.Score.values - 1

colormap = matplotlib.colors.ListedColormap(colors)
plt.scatter(x, y, c=color_indices, cmap=colormap, alpha=0.3)
for score in [0,1,2,3,4]:
    avg_x = np.array(x)[df.Score-1==score].mean()
    avg_y = np.array(y)[df.Score-1==score].mean()
    color = colors[score]
    plt.scatter(avg_x, avg_y, marker='x', color=color, s=100)

plt.title("Amazon ratings visualized in language using t-SNE")

matplotlib 모듈도 visualization 관련 모듈입니다.

이 모듈은 numPy 라이브러리와 같이 많이 쓰입니다.

 

Matplotlib — Visualization with Python

 

Matplotlib — Visualization with Python

seaborn seaborn is a high level interface for drawing statistical graphics with Matplotlib. It aims to make visualization a central part of exploring and understanding complex datasets. statistical data visualization Cartopy Cartopy is a Python package des

matplotlib.org

 

[CCTV] 5.matplotlib기초 (tistory.com)

 

[CCTV] 5.matplotlib기초

서울시 CCTV 분석하기 프로젝트 5. matplotlib기초 matplotlib란? 파이썬의 대표 시각화 도구 Matplotlib는 Python 프로그래밍 언어 및 수학적 확장 NumPy 라이브러리를 활용한 플로팅 라이브러리이다. Tkinter ,

ruby-jieun.tistory.com

필요한 모듈들을 import 한 후에 colors 라는 배열에 5가지 색을 지정했습니다.

그리고 위에서 만들었던 vis_dims 데이터의 값을 x 와 y 변수에 넣습니다.

그러면 2차원으로 축소시킨 임베딩 값의 x는 x 변수에 y는 y 변수에 따로 담깁니다.

 

그리고 데이터 파일의 Score 컬럼에 있는 값에서 1을 뺀 숫자를 color_indices에 넣습니다.

 

다음줄에 있는 matplotlib.colors.ListedColormap() 은 칼라 리스트로부터 Colormap object를 생성하는 함수입니다.

matplotlib.colors.ListedColormap — Matplotlib 3.7.0 documentation

 

matplotlib.colors.ListedColormap — Matplotlib 3.7.0 documentation

Number of entries in the map. The default is None, in which case there is one colormap entry for each element in the list of colors. If the list will be truncated at N. If the list will be extended by repetition.

matplotlib.org

5.차원 축소를 사용한 데이터 압축, 머신러닝교과서, python (tistory.com)

 

5.차원 축소를 사용한 데이터 압축, 머신러닝교과서, python

* 본 포스팅은 머신러닝교과서를 참조하여 작성되었습니다. 5.1 주성분 분석을 통한 비지도 차원 축소 특성 선택 vs 특성 추출 - 원본 특성을 유지한다면 특성 선택 - 새로운 특성 공간으로 데이터

justgwon.tistory.com

다음에 나오는 함수는 matplotlib.pyplot.scatter 함수입니다. 데이터를 좌표에 점으로 표현하는 함수 입니다.

matplotlib.pyplot.scatter — Matplotlib 3.7.0 documentation

 

matplotlib.pyplot.scatter — Matplotlib 3.7.0 documentation

Fundamentally, scatter works with 1D arrays; x, y, s, and c may be input as N-D arrays, but within scatter they will be flattened. The exception is c, which will be flattened only if its size matches the size of x and y.

matplotlib.org

x,y 값과 컬러, 마커모양 line 두께, 테두리 선, 투명도 등을 정의하는 파라미터들을 사용할 수 있습니다.

 

Matplotlib의 Pyplot 모듈로 Scatter Plot 그리기 (glanceyes.com)

 

Matplotlib의 Pyplot 모듈로 Scatter Plot 그리기

2022년 2월 3일(목)부터 4일(금)까지 네이버 부스트캠프(boostcamp) AI Tech 강의를 들으면서 개인적으로 중요하다고 생각되거나 짚고 넘어가야 할 핵심 내용들만 간단하게 메모한 내용입니다. 틀리거

glanceyes.com

좌표위에 점으로 표시하면 그 거리에 따라서 좀 더 가까운 것과 먼것 그리고 가까운 것들의 군집, 다른 군집들과 동떨어져 있는 특정 군집이나 특정 데이터 등을 알 수 있습니다.

 

여기까지 하면 아래와 같은 산점도가 표시 됩니다.

 

1000 개의 점이 있을 겁니다. 2차원화된 임베딩 값을 x,y값으로 해서 표시한 것입니다.

 

다음 나오는 for 루프는 5번 돕니다. 별점이 1점부터 5점까지 5가지가 있기 때문입니다.

위에 color_indices 에서 별점 - 1을 했기 때문에 값은 0~4까지의 숫자들이 있게 됩니다.

여기서 하는 일은 각 별점별로 평균을 내서 좌표로 표시하는 겁니다.

보시면 마커는 X로 돼 있습니다.

 

이 부분만 따로 표시를 하면 아래와 같이 됩니다.

 

각 별점별 평균 값은 좌표상 위와 같이 찍힙니다.

 

그러면 아까 위에 있었던 산점도와 이 별점별 평균 산점도를 같이 표시 하겠습니다.

 

위와 같이 나타납니다.

 

각 별점별 평균 위치와 각 데이터별 위치가 색깔별로 표시 돼 있습니다.

데이터들이 40 근처에는 별로 없습니다.

그리고 별점별 평균은 0-20 사이에 주로 몰려 있구요.

데이터들 중 40-60 사이의 것들이 이 평균과는 좀 떨어져서 군집을 이루고 있네요.

그 안의 별점별 분포도는 특이성을 띄는 것 같지는 않습니다. 다만 빨간색이 거의 없네요.

 

이런 식으로 임베딩 데이터를 t-SNE 로 2차원으로 바꾼 다음 matplotlib.pyplot 으로 시각화 해서 2D로 표시하는 것이 이 예제에서 보여 주는 것 입니다.

 

openai api 에서 받은 임베딩 데이터를 2D 로 시각화 하는 예제였습니다.

openai-cookbook/Visualizing_embeddings_in_2D.ipynb at main · openai/openai-cookbook · GitHub

 

GitHub - openai/openai-cookbook: Examples and guides for using the OpenAI API

Examples and guides for using the OpenAI API. Contribute to openai/openai-cookbook development by creating an account on GitHub.

github.com

 

반응형


반응형

오늘은 Openai cookbook의 embeddings에 있는 Clustering embeddings 예제를 공부해 보겠습니다.

openai-cookbook/Clustering.ipynb at main · openai/openai-cookbook · GitHub

 

GitHub - openai/openai-cookbook: Examples and guides for using the OpenAI API

Examples and guides for using the OpenAI API. Contribute to openai/openai-cookbook development by creating an account on GitHub.

github.com

여기서 사용하는 예제는 아래 글을 보시면 구하실 수 있습니다.

IT 기술 따라잡기 :: Openai cookbook - Embeddings - Text comparison examples - Semantic text search using embeddings (tistory.com)

 

Openai cookbook - Embeddings - Text comparison examples - Semantic text search using embeddings

오늘은 openai cookbook 에 있는 Embeddings 부문의 Text comparison examples 에 있는 Semantic_text_search_using_embeddings.ipynb 예제를 살펴 보겠습니다. 우선 이 예제를 살펴 보기 전에 준비해야 할 사항들이 몇가지

coronasdk.tistory.com

첫번째 예제 코드와 실행 결과는 아래와 같습니다.

 

 

이번에는 command prompt 가 아닌 Jupyter lab을 사용해서 실행해 보았습니다.

이전 예제에서 계속 사용해 왔던 numpy와 pandas 모듈을 import 합니다.

그리고 csv 파일을 datafile_path에 담습니다.

 

그리고 pandas 모듈의 read_csv() 함수를 이용해 해당 파일을 읽어오고 이를 df변수에 담습니다.

그 다음줄은 df에 embedding이라는 컬럼을 만들고 (있으면 덮어쓰고) 그곳에 df.embedding.apply(eval).apply(np.array) 을 담습니다.

string을 numpy array로 변환시키는 부분입니다.

이건 공식처럼 외우셔도 괜찮을 것 같습니다.

아래 페이지에 가면 그 사용법이 나옵니다.

Embeddings - OpenAI API

 

OpenAI API

An API for accessing new AI models developed by OpenAI

platform.openai.com

 

 

그 다음은 df의 embedding의 값을 numpy의 vstack() 함수를 사용해서 행을 기준으로 병합합니다.

numpy.vstack(tup, *, dtype=None, casting='same_kind')

Stack arrays in sequence vertically (row wise).

 

아래 페이지에는 더 자세하게 설명 돼 있습니다.

 

numpy.vstack — NumPy v1.24 Manual

 

numpy.vstack — NumPy v1.24 Manual

If provided, the destination array will have this dtype. Cannot be provided together with out.

numpy.org

그 다음 shape은 배열의 모양을 말합니다. 

이 배열에는 총 1000개의 embedding이 있고 각 embedding 마다 1536개의 float 형식의 값이 있습니다.

그래서 matrix.shape 을 하면 (1000,1536) 이라는 값이 나옵니다.

 

1. Find the clusters using K-means

다음은 clustering을 지원해 주는 python의 모듈인 sklearn을 사용합니다.

sklearn의 cluster에 있는 KMeans라는 함수를 import 합니다.

KMeans와 관련한 설명은 여기에 있습니다.

2.3. Clustering — scikit-learn 1.2.1 documentation

 

2.3. Clustering

Clustering of unlabeled data can be performed with the module sklearn.cluster. Each clustering algorithm comes in two variants: a class, that implements the fit method to learn the clusters on trai...

scikit-learn.org

클러스터 수는 4개로 하고 KMeans를 사용해서 클러스터링 관련 계산을 해서 kmeans에 담습니다.

KMeans는 임의로 중심을 정하는 부분과 모든 데이터에 대해 중심 거리를 각각 구해서 가장 거리가 작은 중심으로 grouping을 하고 각 그룹마다 다시 평균을 구하는 것을 평균의 변화가 거의 없을 때까지 반복합니다.

 

k-평균 알고리즘 - 위키백과, 우리 모두의 백과사전 (wikipedia.org)

 

k-평균 알고리즘 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. k-평균 알고리즘(K-means clustering algorithm)은 주어진 데이터를 k개의 클러스터로 묶는 알고리즘으로, 각 클러스터와 거리 차이의 분산을 최소화하는 방식으로 동작

ko.wikipedia.org

여기서는 그룹을 4개로 나누라는 의미입니다.

 

그 다음에 나오는 fit()과 labels_ 는 항상 따라 다니더라구요.

 

fit()은 k-means clustring을 compute  해 주고 labels_는 각 포인트별로 라벨링을 해 주는 함수 입니다.

 

관련해서는 아래 페이지에 자세한 내용이 있습니다.

 

sklearn.cluster.KMeans — scikit-learn 1.2.1 documentation

 

sklearn.cluster.KMeans

Examples using sklearn.cluster.KMeans: Release Highlights for scikit-learn 1.1 Release Highlights for scikit-learn 1.1 Release Highlights for scikit-learn 0.23 Release Highlights for scikit-learn 0...

scikit-learn.org

 

그 다음에는 데이터 프레임에 Cluster라는 컬럼에 이 labels를 넣습니다.

 

그 다음은 pandas의 데이터를 다루는 함수들이 나옵니다.

Cluster를 기준으로 groupby를 하고 score.mean()으로 평균을 구합니다.

그리고sort_values()로 정렬을 했습니다.

 

 

이 결과는 4개의 그룹으로 clustering을 했고 각 그룹별 평균값일 표시한 겁니다.

 

그 다음 소스코드과 그 소스코드의 실행 결과는 아래와 같습니다.

 

 

sklearn의 manifold  모듈의 TSNE 함수를 import 합니다.

sklearn.manifold.TSNE — scikit-learn 1.2.1 documentation

 

sklearn.manifold.TSNE

Examples using sklearn.manifold.TSNE: Comparison of Manifold Learning methods Comparison of Manifold Learning methods Manifold Learning methods on a severed sphere Manifold Learning methods on a se...

scikit-learn.org

이것은 고차원 데이터를 시각화 해 주는 툴입니다.

 

그 다음은 matplotlib 모듈입니다. 이 모듈도 Visualization 관련 모듈입니다.

Matplotlib — Visualization with Python

 

Matplotlib — Visualization with Python

seaborn seaborn is a high level interface for drawing statistical graphics with Matplotlib. It aims to make visualization a central part of exploring and understanding complex datasets. statistical data visualization Cartopy Cartopy is a Python package des

matplotlib.org

[CCTV] 5.matplotlib기초 (tistory.com)

 

[CCTV] 5.matplotlib기초

서울시 CCTV 분석하기 프로젝트 5. matplotlib기초 matplotlib란? 파이썬의 대표 시각화 도구 Matplotlib는 Python 프로그래밍 언어 및 수학적 확장 NumPy 라이브러리를 활용한 플로팅 라이브러리이다. Tkinter ,

ruby-jieun.tistory.com

 

그 다음은 TSNE()의 값을 tsne 변수에 담고 이것을 fit_transform() 해서 vis_dim2에 담습니다.

fit_transform(X[, y]) Fit X into an embedded space and return that transformed output.

그리고 x와 y를 설정합니다. 

 

그리고 for 문을 통해 각 4개의 그룹별로 루프를 돌게 만들어서 각 그룹별로 색을 다르게 표현하도록 합니다.

 

plt.scatter()는 산점도를 그리는 함수 입니다.

 

matplotlib.pyplot.scatter — Matplotlib 3.7.0 documentation

 

matplotlib.pyplot.scatter — Matplotlib 3.7.0 documentation

Fundamentally, scatter works with 1D arrays; x, y, s, and c may be input as N-D arrays, but within scatter they will be flattened. The exception is c, which will be flattened only if its size matches the size of x and y.

matplotlib.org

[시각화] plt.scatter()를 활용한 산점도 그리기 (tistory.com)

 

[시각화] plt.scatter()를 활용한 산점도 그리기

0. 학습 환경 matplotlib: 3.3.4 seaborn: 0.11.1 금일 학습에는 seaborn 라이브러리에서 제공하는 iris 데이터를 사용하고자 합니다. seaborn에서 제공하는 다른 데이터셋은 '.get_dataset_names()'를 통해 확인이 가

scent-of-light.tistory.com

그리고 plt.title()에서 이 표의 제목을 정해주면 결과와 같은 그림을 얻을 수 있습니다.

 

 

 

4개의 그룹중에 녹색 그룹은 다른 그룹들과 좀 동떨어져 있는 것을 보실 수 있습니다.

 

2. Text samples in the clusters & naming the clusters

지금까지는 raw data를 clustering 하는 법과 이 clustering 한 데이터를 시각화 해서 보여주는 방법을 보았습니다.

 

이제 openai의 api를 이용해서 각 클러스터의 랜덤 샘플들을 보여 주는 코드입니다. 

openai.Completion.create() api를 사용할 것이고 모델 (engine)은 text-ada-001을 사용합니다.

prompt는 아래 질문 입니다.

What do the following customer reviews have in common?

그러면 각 클러스터 별로 review 를 분석한 값들이 response 됩니다.

 

우선 아래 코드를 실행 해 보겠습니다.

import openai

def open_file(filepath):
    with open(filepath, 'r', encoding='utf-8') as infile:
        return infile.read()

openai.api_key = open_file('openaiapikey.txt')

# Reading a review which belong to each group.
rev_per_cluster = 5

for i in range(n_clusters):
    print(f"Cluster {i} Theme:", end=" ")

    reviews = "\n".join(
        df[df.Cluster == i]
        .combined.str.replace("Title: ", "")
        .str.replace("\n\nContent: ", ":  ")
        .sample(rev_per_cluster, random_state=42)
        .values
    )
    response = openai.Completion.create(
        engine="text-ada-001",  #"text-davinci-003",
        prompt=f'What do the following customer reviews have in common?\n\nCustomer reviews:\n"""\n{reviews}\n"""\n\nTheme:',
        temperature=0,
        max_tokens=64,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )
    print(response)

openai를 import 하고 openai api key를 제공하는 부분으로 시작합니다.

그리고 rev_per_cluster는 5로 합니다.

그 다음 for 문에서 n_clusters만큼 루프를 도는데 위에서 n_clusters는 4로 설정돼 있었습니다.

 

reviews에는 Title과 Content 내용을 넣는데 샘플로 5가지를 무작위로 뽑아서 넣습니다.

 

그리고 이 reviews 값을 prompt에 삽입해서 openai.Completion.create() api로 request 합니다.

 

그러면 이 prompt에 대한 response 가 response 변수에 담깁니다.

 

이 response 만 우선 출력해 보겠습니다.

 

Cluster 0 Theme: {
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "text": " Customer reviews:gluten free, healthy bars, content:\n\nThe customer reviews have in common that they save money on Amazon by ordering by themselves by looking for gluten free healthy bars. The bars are also delicious."
    }
  ],
  "created": 1677191195,
  "id": "cmpl-6nEKppB6SqCz07LYTcaktEAgq06hm",
  "model": "text-ada-001",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 44,
    "prompt_tokens": 415,
    "total_tokens": 459
  }
}
Cluster 1 Theme: {
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "text": " Cat food\n\nMessy, undelicious, and possibly unhealthy."
    }
  ],
  "created": 1677191195,
  "id": "cmpl-6nEKpGffRc2jyJB4gNtuCa09dG2GT",
  "model": "text-ada-001",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 15,
    "prompt_tokens": 529,
    "total_tokens": 544
  }
}
Cluster 2 Theme: {
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "text": " Coffee\n\nThe customer's reviews have in common that they are among the best in the market, Rodeo Drive, and that the customer is able to enjoy their coffee half and half because they have an Amazon account."
    }
  ],
  "created": 1677191196,
  "id": "cmpl-6nEKqxza0t8vGRAiK9K5RtCy3Gwbl",
  "model": "text-ada-001",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 45,
    "prompt_tokens": 443,
    "total_tokens": 488
  }
}
Cluster 3 Theme: {
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "text": " Customer reviews of different brands of soda."
    }
  ],
  "created": 1677191196,
  "id": "cmpl-6nEKqKuxe4CVJTV4GlIZ7vxe6F85o",
  "model": "text-ada-001",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 8,
    "prompt_tokens": 616,
    "total_tokens": 624
  }
}
​

이 respons를 보시면 각 Cluster 별로 응답을 받았습니다.

위에 for 문에서 각 클러스터별로 request를 했기 때문입니다.

이제 이 중에서 실제 질문에 대한 답변인 choices - text  부분만 뽑아 보겠습니다.

 

import openai

def open_file(filepath):
    with open(filepath, 'r', encoding='utf-8') as infile:
        return infile.read()

openai.api_key = open_file('openaiapikey.txt')

# Reading a review which belong to each group.
rev_per_cluster = 5

for i in range(n_clusters):
    print(f"Cluster {i} Theme:", end=" ")

    reviews = "\n".join(
        df[df.Cluster == i]
        .combined.str.replace("Title: ", "")
        .str.replace("\n\nContent: ", ":  ")
        .sample(rev_per_cluster, random_state=42)
        .values
    )
    response = openai.Completion.create(
        engine="text-ada-001",  #"text-davinci-003",
        prompt=f'What do the following customer reviews have in common?\n\nCustomer reviews:\n"""\n{reviews}\n"""\n\nTheme:',
        temperature=0,
        max_tokens=64,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )
 
    print(response["choices"][0]["text"].replace("\n", ""))

답변은 아래와 같습니다.

 

Cluster 0 Theme:  Customer reviews:gluten free, healthy bars, content:The customer reviews have in common that they save money on Amazon by ordering by themselves by looking for gluten free healthy bars. The bars are also delicious.
Cluster 1 Theme:  Cat foodMessy, undelicious, and possibly unhealthy.
Cluster 2 Theme:  CoffeeThe customer's reviews have in common that they are among the best in the market, Rodeo Drive, and that the customer is able to enjoy their coffee half and half because they have an Amazon account.
Cluster 3 Theme:  Customer reviews of different brands of soda.

 

다음엔 df에서 샘플을 뽑아 내는 코드가 있습니다.

 

sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42)

df.sample(n) 은 n개의 행들을 임의로 추출합니다.

random_state은 샘플링 결과를 고정시키기 위한 것입니다.

 

[Pandas] 파이썬 데이터프레임 랜덤 샘플링 방법 : df.sample (tistory.com)

 

[Pandas] 파이썬 데이터프레임 랜덤 샘플링 방법 : df.sample

Python의 판다스 모듈로 데이터프레임의 행들을 랜덤 추출할 수 있는 df.sample 기능에 대하여 사용 예제를 정리해보도록 하겠습니다. 이해를 돕기 위하여 아래의 데이터프레임 df에 대하여 행 샘플

jimmy-ai.tistory.com

pandas.DataFrame.sample — pandas 1.5.3 documentation (pydata.org)

 

pandas.DataFrame.sample — pandas 1.5.3 documentation

Default ‘None’ results in equal probability weighting. If passed a Series, will align with target object on index. Index values in weights not found in sampled object will be ignored and index values in sampled object not in weights will be assigned we

pandas.pydata.org

 sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42)

 

여기까지만 실행하면 아래와 같은 결과를 얻습니다.

 

Cluster 0 Theme:      Unnamed: 0   ProductId          UserId  Score  \
117         400  B008JKU2CO  A1XV4W7JWX341C      5   
25          274  B008JKTH2A  A34XBAIFT02B60      1   
722         534  B0064KO16O  A1K2SU61D7G41X      5   
289           7  B001KP6B98   ABWCUS3HBDZRS      5   
590         948  B008GG2N2S  A1CLUIIJL6EHLU      5   

                                               Summary  \
117  Loved these gluten free healthy bars, saved $$...   
25   Should advertise coconut as an ingredient more...   
722                                        very good!!   
289                                  Excellent product   
590                                          delicious   

                                                  Text  \
117  These Kind Bars are so good and healthy & glut...   
25   First, these should be called Mac - Coconut ba...   
722  just like the runts<br />great flavor, def wor...   
289  After scouring every store in town for orange ...   
590  Gummi Frogs have been my favourite candy that ...   

                                              combined  n_tokens  \
117  Title: Loved these gluten free healthy bars, s...        96   
25   Title: Should advertise coconut as an ingredie...        78   
722  Title: very good!!; Content: just like the run...        43   
289  Title: Excellent product; Content: After scour...       100   
590  Title: delicious; Content: Gummi Frogs have be...        75   

                                             embedding  Cluster  
117  [-0.002289338270202279, -0.01313735730946064, ...        0  
25   [-0.01757248118519783, -8.266511576948687e-05,...        0  
722  [-0.011768403463065624, -0.025617636740207672,...        0  
289  [0.0007493243319913745, -0.017031244933605194,...        0  
590  [-0.005802689120173454, 0.0007485789828933775,...        0  
Cluster 1 Theme:      Unnamed: 0   ProductId          UserId  Score  \
536         731  B0029NIBE8  A3RKYD8IUC5S0N      2   
332         184  B000WFRUOC   A22RVTZEIVHZA      4   
424         153  B0007A0AQW  A15X1BO4CLBN3C      5   
298          24  B003R0LKRW  A1OQSU5KYXEEAE      1   
960         589  B003194PBC  A2FSDQY5AI6TNX      5   

                              Summary  \
536  Messy and apparently undelicious   
332                  The cats like it   
424          cant get enough of it!!!   
298               Food Caused Illness   
960          My furbabies LOVE these!   

                                                  Text  \
536  My cat is not a huge fan. Sure, she'll lap up ...   
332  My 7 cats like this food but it is a little yu...   
424  Our lil shih tzu puppy cannot get enough of it...   
298  I switched my cats over from the Blue Buffalo ...   
960  Shake the container and they come running. Eve...   

                                              combined  n_tokens  \
536  Title: Messy and apparently undelicious; Conte...       181   
332  Title: The cats like it; Content: My 7 cats li...        87   
424  Title: cant get enough of it!!!; Content: Our ...        59   
298  Title: Food Caused Illness; Content: I switche...       131   
960  Title: My furbabies LOVE these!; Content: Shak...        47   

                                             embedding  Cluster  
536  [-0.002376032527536154, -0.0027701142244040966...        1  
332  [0.02162935584783554, -0.011174295097589493, -...        1  
424  [-0.007517425809055567, 0.0037251529283821583,...        1  
298  [-0.0011128562036901712, -0.01970377005636692,...        1  
960  [-0.009749102406203747, -0.0068712360225617886...        1  
Cluster 2 Theme:      Unnamed: 0   ProductId          UserId  Score  \
135         410  B007Y59HVM  A2ERWXZEUD6APD      5   
439         812  B0001UK0CM  A2V8WXAFG1TEOC      5   
326         107  B003VXFK44  A21VWSCGW7UUAR      4   
475         852  B000I6MCSY   AO34Q3JGZU0JQ      5   
692         922  B003TC7WN4  A3GFZIL1E0Z5V8      5   

                               Summary  \
135                  Fog Chaser Coffee   
439                    Excellent taste   
326   Good, but not Wolfgang Puck good   
475             Just My Kind of Coffee   
692  Rodeo Drive is Crazy Good Coffee!   

                                                  Text  \
135  This coffee has a full body and a rich taste. ...   
439  This is to me a great coffee, once you try it ...   
326  Honestly, I have to admit that I expected a li...   
475  Coffee Masters Hazelnut coffee used to be carr...   
692  Rodeo Drive is my absolute favorite and I'm re...   

                                              combined  n_tokens  \
135  Title: Fog Chaser Coffee; Content: This coffee...        42   
439  Title: Excellent taste; Content: This is to me...        31   
326  Title: Good, but not Wolfgang Puck good; Conte...       178   
475  Title: Just My Kind of Coffee; Content: Coffee...       118   
692  Title: Rodeo Drive is Crazy Good Coffee!; Cont...        59   

                                             embedding  Cluster  
135  [0.006498195696622133, 0.006776264403015375, 0...        2  
439  [0.0039436533115804195, -0.005451332312077284,...        2  
326  [-0.003140551969408989, -0.009995664469897747,...        2  
475  [0.010913548991084099, -0.014923149719834328, ...        2  
692  [-0.029914353042840958, -0.007755572907626629,...        2  
Cluster 3 Theme:      Unnamed: 0   ProductId          UserId  Score  \
495         831  B0014X5O1C   AHYRTWABDAG1H      5   
978         642  B00264S63G  A36AUU1UNRS48G      5   
916         686  B008PYVINQ  A1DRWYIO7JN1MD      2   
696         926  B0062P9XPU  A33KQALCZGXG8C      5   
491         828  B000EIE20M  A39QHSDUBR8L0T      3   

                               Summary  \
495  Wonderful alternative to soda pop   
978      So convenient, for so little!   
916                    bot very cheesy   
696                         Delicious!   
491                            Just ok   

                                                  Text  \
495  This is a wonderful alternative to soda pop.  ...   
978  I needed two vanilla beans for the Love Goddes...   
916  Got this about a month ago.first of all it sme...   
696  I am not a huge beer lover.  I do enjoy an occ...   
491  I bought this brand because it was all they ha...   

                                              combined  n_tokens  \
495  Title: Wonderful alternative to soda pop; Cont...       273   
978  Title: So convenient, for so little!; Content:...       121   
916  Title: bot very cheesy; Content: Got this abou...        46   
696  Title: Delicious!; Content: I am not a huge be...        97   
491  Title: Just ok; Content: I bought this brand b...        58   

                                             embedding  Cluster  
495  [0.022326279431581497, -0.018449820578098297, ...        3  
978  [-0.004598899278789759, -0.01737511157989502, ...        3  
916  [-0.010750919580459595, -0.0193503275513649, -...        3  
696  [0.009483409114181995, -0.017691848799586296, ...        3  
491  [-0.0023960231337696314, -0.006881058216094971...        3

여기서 데이터를 아래와 같이 가공을 합니다.

 

    for j in range(rev_per_cluster):
        print(sample_cluster_rows.Score.values[j], end=", ")
        print(sample_cluster_rows.Summary.values[j], end=":   ")
        print(sample_cluster_rows.Text.str[:70].values[j])

Score의 값들을 가지고 오고 끝에는 쉼표 , 를 붙입니다.

그리고 Summary의 값을 가지고 오고 끝에는 : 를 붙입니다.

그리고 Text컬럼의 string을 가지고 오는데 70자 까지만 가지고 옵니다.

 

전체 결과를 보겠습니다.

 

Cluster 0 Theme:  Customer reviews:gluten free, healthy bars, content:The customer reviews have in common that they save money on Amazon by ordering by themselves by looking for gluten free healthy bars. The bars are also delicious.
5, Loved these gluten free healthy bars, saved $$ ordering on Amazon:   These Kind Bars are so good and healthy & gluten free.  My daughter ca
1, Should advertise coconut as an ingredient more prominently:   First, these should be called Mac - Coconut bars, as Coconut is the #2
5, very good!!:   just like the runts<br />great flavor, def worth getting<br />I even o
5, Excellent product:   After scouring every store in town for orange peels and not finding an
5, delicious:   Gummi Frogs have been my favourite candy that I have ever tried. of co
Cluster 1 Theme:  Cat foodMessy, undelicious, and possibly unhealthy.
2, Messy and apparently undelicious:   My cat is not a huge fan. Sure, she'll lap up the gravy, but leaves th
4, The cats like it:   My 7 cats like this food but it is a little yucky for the human. Piece
5, cant get enough of it!!!:   Our lil shih tzu puppy cannot get enough of it. Everytime she sees the
1, Food Caused Illness:   I switched my cats over from the Blue Buffalo Wildnerness Food to this
5, My furbabies LOVE these!:   Shake the container and they come running. Even my boy cat, who isn't 
Cluster 2 Theme:  CoffeeThe customer's reviews have in common that they are among the best in the market, Rodeo Drive, and that the customer is able to enjoy their coffee half and half because they have an Amazon account.
5, Fog Chaser Coffee:   This coffee has a full body and a rich taste. The price is far below t
5, Excellent taste:   This is to me a great coffee, once you try it you will enjoy it, this 
4, Good, but not Wolfgang Puck good:   Honestly, I have to admit that I expected a little better. That's not 
5, Just My Kind of Coffee:   Coffee Masters Hazelnut coffee used to be carried in a local coffee/pa
5, Rodeo Drive is Crazy Good Coffee!:   Rodeo Drive is my absolute favorite and I'm ready to order more!  That
Cluster 3 Theme:  Customer reviews of different brands of soda.
5, Wonderful alternative to soda pop:   This is a wonderful alternative to soda pop.  It's carbonated for thos
5, So convenient, for so little!:   I needed two vanilla beans for the Love Goddess cake that my husbands 
2, bot very cheesy:   Got this about a month ago.first of all it smells horrible...it tastes
5, Delicious!:   I am not a huge beer lover.  I do enjoy an occasional Blue Moon (all o
3, Just ok:   I bought this brand because it was all they had at Ranch 99 near us. I

이제 좀 보기 좋게 됐습니다.

 

이번 예제는 raw 데이터를 파이썬의 여러 모듈들을 이용해서 clustering을 하고 이 cluster별로 openai.Completion.create() api를 이용해서 궁금한 답을 받는 일을 하는 예제를 배웠습니다.

 

큰 raw data를 카테고리화 해서 나누고 이에 대한 summary나 기타 정보를 Completion api를 통해 얻을 수 있는 방법입니다.

 

전체 소스코드는 아래와 같습니다.

 

# imports
import numpy as np
import pandas as pd

# load data
datafile_path = "./data/fine_food_reviews_with_embeddings_1k.csv"

df = pd.read_csv(datafile_path)
df["embedding"] = df.embedding.apply(eval).apply(np.array)  # convert string to numpy array
matrix = np.vstack(df.embedding.values)
matrix.shape

from sklearn.cluster import KMeans

n_clusters = 4

kmeans = KMeans(n_clusters=n_clusters, init="k-means++", random_state=42)
kmeans.fit(matrix)
labels = kmeans.labels_
df["Cluster"] = labels

df.groupby("Cluster").Score.mean().sort_values()

from sklearn.manifold import TSNE
import matplotlib
import matplotlib.pyplot as plt

tsne = TSNE(n_components=2, perplexity=15, random_state=42, init="random", learning_rate=200)
vis_dims2 = tsne.fit_transform(matrix)

x = [x for x, y in vis_dims2]
y = [y for x, y in vis_dims2]

for category, color in enumerate(["purple", "green", "red", "blue"]):
    xs = np.array(x)[df.Cluster == category]
    ys = np.array(y)[df.Cluster == category]
    plt.scatter(xs, ys, color=color, alpha=0.3)

    avg_x = xs.mean()
    avg_y = ys.mean()

    plt.scatter(avg_x, avg_y, marker="x", color=color, s=100)
plt.title("Clusters identified visualized in language 2d using t-SNE")

import openai

def open_file(filepath):
    with open(filepath, 'r', encoding='utf-8') as infile:
        return infile.read()

openai.api_key = open_file('openaiapikey.txt')

# Reading a review which belong to each group.
rev_per_cluster = 5

for i in range(n_clusters):
    print(f"Cluster {i} Theme:", end=" ")

    reviews = "\n".join(
        df[df.Cluster == i]
        .combined.str.replace("Title: ", "")
        .str.replace("\n\nContent: ", ":  ")
        .sample(rev_per_cluster, random_state=42)
        .values
    )
    response = openai.Completion.create(
        engine="text-ada-001",  #"text-davinci-003",
        prompt=f'What do the following customer reviews have in common?\n\nCustomer reviews:\n"""\n{reviews}\n"""\n\nTheme:',
        temperature=0,
        max_tokens=64,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )
 
    print(response["choices"][0]["text"].replace("\n", ""))

    sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42)

    for j in range(rev_per_cluster):
        print(sample_cluster_rows.Score.values[j], end=", ")
        print(sample_cluster_rows.Summary.values[j], end=":   ")
        print(sample_cluster_rows.Text.str[:70].values[j])
반응형
이전 1 다음