working_helen

[카테고리 태깅] GPT API를 이용한 리뷰 내용 카테고리 태깅 본문

deep daiv./NLP project

[카테고리 태깅] GPT API를 이용한 리뷰 내용 카테고리 태깅

HaeWon_Seo 2024. 11. 15. 23:29

앞서 키워드 추출을 통해 선정한 6가지 리뷰 내용 카테고리를 활용한다. 프롬프트 엔지니어링을 사용해 각 리뷰마다 내용에서 6가지 카테고리 중 해당되는 일부분이 있다면 이를 태깅하여 데이터셋으로 저장하는 과정을 진행한다.

 

 

1. GPT API few-shot prompting

2. 카테고리 태깅 결과 

 


📢 카테고리 태깅 과정, 감정분석, 요약 과정
- Llama의 수행 시간 및 코랩 GPU 제한, GPT API 호출 비용으로 인해 150만개 리뷰 전체를 다 사용 불가 
- 23년도 이후 & 4단어 이상 리뷰들만 선택
  → ’도움돼요’ 개수가 많은 순, 최신순으로 정렬 후 각 상품마다 상위 50개의 리뷰만 사용

 

 

 

 

1. GPT API few-shot prompting

GPT API few-shot prompting으로 마켓컬리 리뷰 카테고리 태깅 시도

 

LLM에게 요구할 task

- 앞서 키워드 추출로 선정한 리뷰 내용 카테고리 : 맛/향/풍미, 보관, 포장/배송, 양, 가격, 활용 방법

- 주어진 리뷰에서 각 카테고리에 해당되는 내용을 찾아서 태깅하는 task

- 카테고리 태깅 task 수행 결과 예시 

 

 

(1) few-shot 예제 선택

 

- 예제로 사용할 리뷰 데이터를 선정한 기준

  • 각각 카테고리로 태깅되는 내용이 다양하게 존재하는 리뷰를 선택
  • 긍정적인 내용의 리뷰와 부정적인 내용의 리뷰를 고르게 포함
  • 어떠한 카테고리로도 태깅된 내용이 없는 경우 빈 dictionary로 답변하는 예제도 포함 
  • GPT API에 일부 리뷰들에 대한 카테고리 태깅을 사전 실험
    → GPT가 잘 탐지하지 못한 or 잘 분류하지 못한 내용의 리뷰들을 예제로 사용
        예제를 통해 잘 태깅하지 못했던 리뷰들에 대해 잘 판단할 수 있도록 유도

- 사용한 few-shot 예제의 일부 

더보기

example = """
review
'''몇 번째 내 돈 내 산인지 모르겠어요 진짜 쟁여템입니다
이렇게까지 내용물이 풍부한 밀 키트는 처음이에요 간도 딱 맞고 양도 푸짐하고 게다가 양이 진짜 많이 들어서 먹을 때마다 감동입니다.
고기의 품질도 너무 좋아요 보통은 국에 흩어지는 고기들이 많은데 제대로 모양을 갖춘 고기가 한가득 들어 있어서 양 곰탕 집에 와서 먹는 기분이 들어요'''
category_labels
{"진짜 쟁여템입니다":'보관',
"내용물이 풍부한 밀 키트는 처음이에요":'양',
"간도 딱 맞고":'맛/향/풍미',
"양도 푸짐하고":'양',
"양이 진짜 많이 들어서":'양',
"고기의 품질도 너무 좋아요":'맛/향/풍미',
"제대로 모양을 갖춘 고기가 한가득 들어 있어서":'양',
"양 곰탕 집에 와서 먹는 기분이 들어요":'맛/향/풍미'}


review
'''갈비탕 할인하면 한 두 개씩 사 둬요 냉동이라 더 좋고 대파나 팽이 마늘 넣어 먹으면 더 좋아요
생각보다 실해요 아침에 바쁘고 간단히 차려 먹고 싶을 때 딱 좋네요 집에서 간편하게 해먹기 좋아요
첨가물이 섞여 있지 않은 것도 좋아서 항상 비상식량으로 쟁여둡니다'''
category_labels
{"갈비탕 할인하면 한 두 개씩 사 둬요":'보관',
"냉동이라 더 좋고":'보관',
"대파나 팽이 마늘 넣어 먹으면 더 좋아요":'활용 방법'
"생각보다 실해요":'양',
"아침에 바쁘고 간단히 차려 먹고 싶을 때 딱 좋네요":'활용 방법',
"집에서 간편하게 해먹기 좋아요":'활용 방법',
"첨가물이 섞여 있지 않은 것도 좋아서":'맛/향/풍미',
"항상 비상식량으로 쟁여둡니다":'보관'}


review
'''맛은 그럭저럭인데 포장지는 신경 쓰여야할 것 같습니다 컬리에서 많이 시키는데 해동 후 이렇게 뚫려 내용물이 세어 나온 적은 처음입니다 후기 찾아 보니 저처럼 뚫린 분이 계신 듯합니다'''
category_labels
{"포장지는 신경 쓰여야할 것 같습니다":'포장/배송',
"해동 후 이렇게 뚫려 내용물이 세어 나온 적은 처음입니다":'포장/배송'}


review
'''내용물 봉지에 이물질인지 벌레인지 묻어 있어서 불쾌합니다 두 개나 굵고 진한 머리카락인가요
국물은 진해 보이지만 찜찜하고 먹기 꺼려져서 버렸어요 위생 상태 별로네요```
category_labels
{"내용물 봉지에 이물질인지 벌레인지 묻어 있어서 불쾌합니다":'포장/배송',
"두 개나 굵고 진한 머리카락인가요":'포장/배송',
"찜찜하고 먹기 꺼려져서 버렸어요 위생 상태 별로네요":'포장/배송'}


review
'''가끔 구매해요 가족 모두 만족 감사합니다 또 사러 올게요'''
category_labels
{}
"""

 

 

(2) prompt 작성

 

- 프롬프트 성능을 위해 사용한 작성 방식

  • '파트'라는 용어를 직접적으로 제시함으로써 모델이 전체 문장에서 일부분을 변형없이 그대로 추출하도록 유도
  • 너무 길이가 긴 '파트'를 하나의 카테고리로 분류해버리지 않도록 각 '파트'의 단어 개수를 15개로 한정
  • 각 '파트'에서 가장 중심적으로 드러나는 카테고리 하나로만 태깅하도록 제한 
  • 주어진 리뷰에서 '파트'가 없는 특별한 케이스에 대해 적절히 처리할 수 있도록 '빈 dictionary를 출력'하라는 명령을 직접적으로 추가
  • 출력 형식을 'dictionary'로 지정, 예제에서 정답도 'dictionary' 형태로 제시
  • API 호출 시간 및 비용을 절약하기 위해 모델의 성능을 저하시키지 않는 선에서 예제와 프롬프트에 사용되는 토큰을 최소화 
prompt = f"""
카테고리에는 ['맛/향/풍미', '양', '포장/배송', '보관', '가격', '활용 방법'] 항목이 있다.
주어진 review 속 상품에 관한 내용 중 카테고리의 각 항목과 관련되어 있는 일부분인 '파트'를 찾은 후
찾은 '파트'에 카테고리 항목을 태깅한 결과를 dictionary 형태의 category_labels로 출력하라.

'파트'의 단어 개수는 15개가 넘지 않는다.
각 '파트'에는 하나의 카테고리 항목만 태깅한다.
어떠한 항목에도 태깅되는 내용이 없으면 빈 dictionary를 출력한다.

예시:{example}
"""

 

 

 

 

(3) 카테고리 태깅 진행 

 

- GPT API 호출 : gpt-4o-mini 모델 사용

  • Temperature = 0 : 모델 답변의 무작위성을 최소화, 정확하고 일관적인 답변을 생성하기 위해 
model = ChatOpenAI(
    model_name="gpt-4o-mini",
    temperature=0,
    max_tokens=1000,
    openai_api_key="openai_api_key"
)

 

- GPT 답변 생성

  • delimiters(''' ''')를 사용해 입력되는 리뷰 내용을 모델이 인식하기 좋도록 구분
  • system role로 assistant의 구체적인 페르소나 설정
    user role로 assistant가 따라야할 prompt 지시문 전달 
  • model.invoke로 입력에 대한 응답 텍스트를 생성     
def tagging(text):
    prompting = (
    f"{prompt}\n"
    "문제:\n"
    "review\n"
    f"'''{text}'''\n"
    "category_labels\n"  
    )

    messages = [("system", "You are a consumer analyst extracting meaningful phrases from reviews and categorizing them."),
                ("user", prompting)]
    
    response = model.invoke(messages)     
    return response

 

- GPT가 생성한 답변을 데이터프레임 형태로 저장 

# JSON 리턴을 dictionary 형태로 변환하는 함수 
def JSON_parsing(text):
    json_str = text.replace("'", '"')
    json_str = re.sub(r'\\+', '', json_str)
    return json.loads(json_str)
    
data=[]
for i in range(df.shape[0]):
    text = df.loc[i, 'cleaned_Kiwi_review']
    response = tagging(text)
    cate_dict = JSON_parsing(response.content)
    row = {'index':i, 'review': text, 'tagging':cate_dict}
    data.append(row)
result = pd.DataFrame(data)

 

 

 

 

 

2. 카테고리 태깅 결과 

result 데이터프레임

 

태깅 결과 예시 

전에 정말 맛있게 먹어서 재주문합니다
이름 그대로 정말 넉넉하게 들어 있어요
둘이 먹어도 충분할 정도로 건더기도 많이 들어 있고 누린내도 하나도 없고 정말 맛있습니다
순대국 좋아하시면 추천 드려요

{'정말 맛있게 먹어서 재주문합니다': '맛/향/풍미',
'정말 넉넉하게 들어 있어요': '양',
'둘이 먹어도 충분할 정도로 건더기도 많이 들어 있고': '양',
'누린내도 하나도 없고 정말 맛있습니다': '맛/향/풍미',
'순대국 좋아하시면 추천 드려요': '맛/향/풍미'}
전날 미리 냉장 실에 넣어서 해동해 두고 잤어요
아침에 휘리릭 끓여서 먹으려구요 
전날 남편이랑 술 한 잔 하고 자면 아침에 일어나서 밥 하기 귀찮잖아요 
그냥 봉지 뜯고 끓이기만하니까 완성이라서 너무 좋았어요 5분 만에 아침 해결
맛은 진한 황태 해장국 맛이었어 요 
국물은 굉장히 진한 편이고 뽀얀 국물 건더기가 정말 많더라구요
바쁜 아침에 든든한 한 끼로도 좋겠지만 전 뭐니 뭐니 해도 해장용으로 진짜 더 없이 좋을 것 같더라구요 
처음 시켜 본 거라서 오리지널 그대로 끓이기만해서 먹어 봤는데 먹어 보니 제가 두부를 좋아해서 두부 찹찹 썰어 넣어서 끓여도 맛있을 것 같네요
건더기도 국물 양도 넉넉해서 너무 좋아요
국밥으로 안 먹고 사이드 국으로 먹으면 23 인까지도 먹을 수 있을 것 같아요

{'전날 미리 냉장 실에 넣어서 해동해 두고 잤어요': '보관',
'아침에 휘리릭 끓여서 먹으려구요': '활용 방법',
'그냥 봉지 뜯고 끓이기만하니까 완성이라서 너무 좋았어요 5분 만에 아침 해결': '활용 방법',
'맛은 진한 황태 해장국 맛이었어 요': '맛/향/풍미',
'국물은 굉장히 진한 편이고 뽀얀 국물 건더기가 정말 많더라구요': '양',
'바쁜 아침에 든든한 한 끼로도 좋겠지만 전 뭐니 뭐니 해도 해장용으로 진짜 더 없이 좋을 것 같더라구요': '활용 방법',
'먹어 보니 제가 두부를 좋아해서 두부 찹찹 썰어 넣어서 끓여도 맛있을 것 같네요': '활용 방법',
'건더기도 국물 양도 넉넉해서 너무 좋아요': '양',
'국밥으로 안 먹고 사이드 국으로 먹으면 23 인까지도 먹을 수 있을 것 같아요': '양'}
고기는 3개밖에 안 들어 있는데 그마저도 고기 사이즈가 너무 작아서
세일해서 샀는데도 너무 돈 아까워요
맛도 그냥저냥
고춧가루랑 다진 마늘 넣어 먹었어요

{'고기는 3개밖에 안 들어 있는데 그마저도 고기 사이즈가 너무 작아서': '양',
'세일해서 샀는데도 너무 돈 아까워요': '가격', '맛도 그냥저냥': '맛/향/풍미',
'고춧가루랑 다진 마늘 넣어 먹었어요': '활용 방법'}
후기 좋아 구입했어요
맛은 어떤 양념도 첨가하지 않은 순수한 탕 맛이라 맘에 들지만 고기는 많이 우려서인지 맛이 안 느껴지고 도가니도 조금밖에 안 들어가서 아쉬웠어요

{'맛은 어떤 양념도 첨가하지 않은 순수한 탕 맛이라 맘에 들지만': '맛/향/풍미',
'고기는 많이 우려서인지 맛이 안 느껴지고': '맛/향/풍미',
'도가니도 조금밖에 안 들어가서 아쉬웠어요': '양'}
항상 포장 상태를 보면서 참
교육도 안 하나 하는 생각을 했습니다
박스 맨 밑에다 쿠션처럼 야채를 깔고 그 위에 우유 바나나 고기 순두부 얼음 2개를 얹어서 배달했네요
포장 상태를 하나 하나 빼면서 찍었어요 
그것까지도 화가 나는데
봉지가 찢어지고 야채가 새어서 박스 바닥에 흩어져 있어요
이런 걸 어떻게 먹어요
야채는 말라서 습기가 없었어요

{'항상 포장 상태를 보면서 참': '포장/배송',
'박스 맨 밑에다 쿠션처럼 야채를 깔고 그 위에 우유 바나나 고기 순두부 얼음 2개를 얹어서 배달했네요': '포장/배송',
'봉지가 찢어지고 야채가 새어서 박스 바닥에 흩어져': '포장/배송',
'야채는 말라서 습기가 없었어요': '포장/배송'}

 

 

➡️ gpt-4o-mini 모델을 사용한 경우에도 어느정도 좋은 성능의 분류 결과를 보임 

      gpt-4o-mini prompting을 통한 리뷰 내용 카테고리 태깅 결과 사용 

 

카테고리 태깅 결과 dataframe 

 

 

 

 

 

 

 

Reference

https://www.promptingguide.ai/kr/techniques/fewshot

https://www.ibm.com/think/topics/few-shot-prompting#Advantages+and+limitations+of+few+shot+prompting