본문 바로가기

파이썬 & 머신러닝과 딥러닝

자연어 처리(NLP) - 감성분석, OpenAI

 

텍스트 데이터를 딥러닝 모델에 넣을 수 있는 형식으로 변환

 

데이터 준비 

from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk.corpus import wordnet as wn
import nltk
from nltk.corpus import stopwords

nltk.download("punkt")
nltk.download('wordnet')
nltk.download('stopwords')

def penn_to_wn(tag):
    if tag.startswith("J"):
        return wn.ADJ
    elif tag.startswith("N"):
        return wn.NOUN
    elif tag.startswith("R"):
        return wn.ADV
    elif tag.startswith("V"):
        return wn.VERB

def words_lemmatizer(pos_tagged_words):
    lemmatizer = WordNetLemmatizer()
    lemmatized_words = []
    for word, tag in pos_tagged_words:
        wn_tag = penn_to_wn(tag)
        if wn_tag in (wn.NOUN, wn.ADJ, wn.ADV, wn.VERB):
            stem = lemmatizer.lemmatize(word, wn_tag)
            lemmatized_words.append(stem)
        else:
            lemmatized_words.append(word)
    return lemmatized_words

def clean_by_freq(tokenized_words, cut_off_count):
    vocab = Counter(tokenized_words)

    # 빈도수가 cut_off_count 이하인 단어를 제거하는 코드를 작성해 주세요
    uncommon_words = {key for key, value in vocab.items() if value <= cut_off_count}
    cleaned_words = [word for word in tokenized_words if word not in uncommon_words]

    return cleaned_words


def clean_by_len(tokenized_words, cut_off_length):
    cleaned_words = []

    for word in tokenized_words:
        # 길이가 cut_off_length 이하인 단어 제거하는 코드를 작성해 주세요
        if len(word) > cut_off_length:
            cleaned_words.append(word)

    return cleaned_words

def clean_by_freq(tokenized_words, cut_off_count):
    vocab = Counter(tokenized_words)

    # 빈도수가 cut_off_count 이하인 단어를 제거하는 코드를 작성해 주세요
    uncommon_words = {key for key, value in vocab.items() if value <= cut_off_count}
    cleaned_words = [word for word in tokenized_words if word not in uncommon_words]

    return cleaned_words


def clean_by_len(tokenized_words, cut_off_length):
    cleaned_words = []

    for word in tokenized_words:
        # 길이가 cut_off_length 이하인 단어 제거하는 코드를 작성해 주세요
        if len(word) > cut_off_length:
            cleaned_words.append(word)

    return cleaned_words

def pos_tagger(tokenized_sents):
    pos_tagged_words = []

    for sentence in tokenized_sents:
        # 단어 토큰화
        tokenized_words = word_tokenize(sentence)
    
        # 품사 태깅
        pos_tagged = pos_tag(tokenized_words)
        pos_tagged_words.extend(pos_tagged)
    
    return pos_tagged_words


# 불용어 제거 함수
def clean_by_stopwords(tokenized_words, stop_words_set):
    cleaned_words = []
    
    for word in tokenized_words:
        if word not in stop_words_set:
            cleaned_words.append(word)
            
    return cleaned_words
import pandas as pd
from nltk.tokenize import sent_tokenize
from nltk.tag import pos_tag
from nltk.corpus import stopwords
nltk.download("stopwords")

# 불용어 제거 함수
def clean_by_stopwords(tokenized_words, stop_words_set):
    cleaned_words = []
    
    for word in tokenized_words:
        if word not in stop_words_set:
            cleaned_words.append(word)
            
    return cleaned_words

stopwords_set = set(stopwords.words("english"))

df = pd.read_csv("imdb.tsv", sep = '\t')
del df['Unnamed: 0']

df['review'] = df['review'].str.lower() # 소문자 처리
df["sent_tokens"] = df['review'].apply(sent_tokenize) # 문장 토큰화
df["pos_tagged_tokens"] = df['sent_tokens'].apply(pos_tagger) # 형태소
df["lemmatized_tokens"] = df['pos_tagged_tokens'].apply(words_lemmatizer)

df["cleaned_tokens"] = df['lemmatized_tokens'].apply(lambda x : clean_by_len(x, 2))
df['cleaned_tokens'].apply(lambda x : clean_by_stopwords(x, stopwords_set))

df

 

 

단어별로 라벨링

# 단어별로 라벨링
# 빈도가 많이 나온 단어부터 라벨링(중요)
from collections import Counter

words = []
for i in df['cleaned_tokens']:
    words += i

vocab = Counter(words) # 빈도수 계산
vocab = vocab.most_common() # 빈도 높은 순서대로 정렬

word_to_idx = {}
for word, freq in vocab:
    word_to_idx[word] = len(word_to_idx) + 1

word_to_idx

 

# 단어 토큰들을 숫자 인덱스로 변환하는 함수
def idx_encoder(tokens, word_to_idx):
    idx_box = []
    for i in tokens:
        idx_box.append(word_to_idx[i])
    return idx_box
df['integer_encoded'] = df['cleaned_tokens'].apply(lambda x : idx_encoder(x, word_to_idx))
df

 

 

각 문장의 길이를 고정된 길이로 맞추기 (짧은 시퀀스는 0으로 패딩)

max_len = 0
for i in df['integer_encoded']:
    if len(i) >= max_len:
        max_len = len(i)

max_len

 

for tokens in df['integer_encoded']:
    while len(tokens) < max_len:
        tokens.append(0)

for i in df['integer_encoded']:
    print(len(i))

 


 

감성분석

wordnet(감성사전) : 단어랑 의미

SentiWordNet : 감성 분석을 위한 감정 점수(긍정, 부정, 중립)를 제공

 

# 감성분석 
# wordnet(감성사전) : 단어랑 의미
from nltk.corpus import wordnet as wn

word = wn.synsets('lead') # 동의어 집합(synonym set)을 반환
word

 

wn.synset('lead.n.01').definition() # 명사 첫번째 의미에 대한 정의 -> 납으로 쓰임

 

from nltk.corpus import sentiwordnet as swn
nltk.download('sentiwordnet')

a = list(swn.senti_synsets('happy'))[0]

a.pos_score() - a.neg_score()

→ "happy"라는 단어가 긍정적인 감정을 더 많이 가지고 있음

 

 

NLTK의 wordnet과 sentiwordnet을 사용하여

"hard"의 형용사(ADJ)와 부사(ADV) 형태에 대한 감성 점수를 비교

from nltk.corpus import wordnet as wn
from nltk.corpus import sentiwordnet as swn
import nltk

# 단어 'hard'의 형용사와 부사에 대한 SentiWordNet 객체 생성
a = wn.synsets('hard', wn.ADJ)[0]
b = wn.synsets('hard', wn.ADV)[0]

# SentiWordNet 객체 생성
a2 = swn.senti_synset(a.name())
b2 = swn.senti_synset(b.name())

# 감성 점수 출력
print("형용사 'hard'의 감성 점수:", a2)
print("부사 'hard'의 감성 점수:", b2)

 

pos_tagged_words = df['pos_tagged_tokens'][0] # (단어, 품사 태그)의 튜플 리스트 가져오
score = 0

for word, tag in pos_tagged_words:
    tag = penn_to_wn(tag) # Penn Treebank 품사 태그를 WordNet 품사 태그로 변환
    if tag not in (wn.NOUN, wn.ADJ, wn.ADV, wn.VERB): # 유효한 품사(명사, 형용사, 부사, 동사)인 확인
        continue 
    if not wn.synsets(word, tag): # 해당 단어와 품사에 대한 synset(의미 집합)이 존재하는지 확인
        continue
    else:
        synsets = wn.synsets(word, tag)

    synset = synsets[0] # 첫 번째 synset을 선택하여 감성 점수를 가져오
    senti = swn.senti_synset(synset.name())
    senti2 = senti.pos_score() - senti.neg_score()
    
    score += senti2

print(score)

 

 

  • 주어진 단어 목록에서 각 단어에 대해 유효한 품사인지 확인하고, WordNet에서 해당 단어의 의미 집합 찾기
  • 각 단어의 감성 점수를 계산하여 총 점수에 누적
  • 최종적으로 감성 점수를 출력

 


 

df에 감성점수 열 추가하기

# df에 감성점수 열 추가하기!

def check_senti_score(tokens):
    pos_tagged_words = tokens
    score = 0
    for word, tag in pos_tagged_words:
        tag = penn_to_wn(tag)
        if tag not in (wn.NOUN, wn.ADJ, wn.ADV, wn.VERB):
            continue
        if not wn.synsets(word, tag):
            continue
        else:
            synsets = wn.synsets(word, tag)
    
        synset = synsets[0]
        senti = swn.senti_synset(synset.name())
        senti2 = senti.pos_score() - senti.neg_score()
        
        score += senti2
    
    return score
df['Senti Score'] = df['pos_tagged_tokens'].apply(check_senti_score)
df

 


VADER 감정 분석기

감정 분석기를 사용하여 세 가지 문장에 대한 감정 점수를 계산

from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')

analyzer = SentimentIntensityAnalyzer()

text1 = 'This is a great movie!'
text2 = 'This is a terrible movie!'
text3 = 'This movie was just okay'

print(analyzer.polarity_scores(text1))
print(analyzer.polarity_scores(text2))
print(analyzer.polarity_scores(text3))

def vader_sentiment(text):
    analyzer = SentimentIntensityAnalyzer()
    score = analyzer.polarity_scores(text)['compound']
    return score

df['Vader Score'] = df['review'].apply(vader_sentiment)
df

 


 

한국어 감성분석

# 한국어 감성분석 (군산대학)
knu = pd.read_csv('knu_sentiment_lexicon.csv')
knu_dic = dict(zip(knu['word'], knu['polarity']))

text = '이 제품 정말 좋아요'

def sentiment_analysis(text):
    score = 0
    for key, value in knu_dic.items():
        if key in text:
            score += value * text.count(key)
    return score
sentiment_analysis(text)

 


 

OpenAI

 

# Python + ChatGPT
# 1. 저렴하다
# 2. 공식사이트보다 더 높은 버전 사용 가능
#!pip install openai

from openai import OpenAI

openai = OpenAI(api_key = 'sk-g27B9BcTnEABs_w0-T5RPTXOAEzOxEUD-uMOmUP3CST3BlbkFJXFIK4FwCziu0DmNEOUpgPJjDJPobLv2eSLV3HlyYsA')

box = [{"role" : "system", "content" : "너는 포켓몬마스터야."},
       {"role" : "user", "content" : "제일 약한 포켓몬이 누구야?"}]

while True:

    a = input("입력 : ")
    if a == "종료":
        break

    box.append({"role" : "user", "content" : a})
    
    model = openai.chat.completions.create(
        model = "chatgpt-4o-latest",
        messages = box
    )

    result = model.choices[0].message.content
    box.append({"role" : "assistant", "content" : result})

    print(result)

 

 

그림 그리기

model = openai.images.generate(
    model = 'dall-e-3',
    prompt = "베트남으로 여행간 여행객이 지쳐서 쉬고있는 모습을 일본 애니매이션 스타일로 여자 캐릭터를 귀엽게 그려줘.",
    size = "1024x1024",
    n = 1
)

 

 

주어진 이미지 변형, 확장

model = openai.images.create_variation(
    model = 'dall-e-2',
    image = open("피카소.png", 'rb'),
    n = 1,
    size = "1024x1024"
)