Can only use .str accessor with string values!

all_data['HSCODE']=all_data['HSCODE'].str.replace(pat=r'[ㄱ-ㅣ가-힣]+', repl= r'', regex=True)

-->all_data['HSCODE']=all_data['HSCODE'].astype(str).str.replace(pat=r'[ㄱ-ㅣ가-힣]+', repl= r'', regex=True)

 

java.nio.file.InvalidPathException: Illegal char <*> at index 55: D:\ProgramData\Anaconda3\Lib\site-packages\konlpy\java\*

이런 오류나서 

pip install "jpype1<1" 

사용했더니 오류나서면 --user사용해라고해서

pip install "jpype1<1" --user

사용해서 오류 안 나길래 되나 해서 갔더니

안됐다.

 

java.lang.UnsatisfiedLinkError: Native Library D:\ProgramData\Anaconda3\Lib\site-packages\_jpype.cp38-win_amd64.pyd already loaded in another classloader

그래서 구글링 했더니 

https://daewonyoon.tistory.com/386

 

2021년 5월 konlpy 설치시 문제점들

konlpy 를 오랜만에 설치하고 테스트해 보았다. 설치시에 문제점들이 발견되어 정리하여 공유한다. 테스트는 윈도우에서 수행하였고, openjdk 가 (adoptopenjdk를 이용) 깔려 있으며, JAVA_HOME 과 PATH 설정

daewonyoon.tistory.com

파이썬 3.8.5부터 jpype1 0.7.5버전에서 에러가 발생한다고 한다.

jpype1을 1.0.2로 올리는

pip install -U "jpype1<1.1" --user

사용했더니 가능했다. 

 

 

keras는 오픈소스 신경망 라이브러리다. 

https://ko.wikipedia.org/wiki/%EC%BC%80%EB%9D%BC%EC%8A%A4

 

케라스 - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

tensorflow의 버전이 달라 연동이 안 되는 점을 조정해준다고 한다. 

%tensorflow_version 2.x

#구성 바뀌어도 keras가 조정해줌
from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

print(train_images.shape)
print(test_images.shape)

keras의 내장된 데이터를 train_images, train_labels와 같이 불러들인다 

 

MNIST데이터는 특성을 표현하는 부분을 2차원으로 구성되어 있다. 

print(test_images[1])

-> 줄이 다음 칸으로 밀려 정확한 확인이 어려움

 

데이터 타입은 넘파이 배열임

import numpy as np
np.set_printoptions(linewidth=np.inf )
  • 한 줄에 출력될 문자의 최대수를 늘린다. 

 

  • 두 개의 차원을 이용하여 숫자 '2' 표현
  • 각 셀의 숫자는 색상 값.
  • 숫자로 대상을 인식한다. 
import matplotlib.pyplot as plt
plt.imshow(test_images[1])

 

Layer 쌓기

from tensorflow.keras import models
from tensorflow.keras import layers
model = models.Sequential() # 모델 초기화 ( 새로운 모델을 만들 준비)

# 은닉층 설정
# 처음에는 입력 shape을 설정한다. 3차원 데이터를 2차원으로 변형할 것이다. input_shape=(특성의 수, 샘플의 수)
# 단, 샘플의 개수는 몇 개가 올지 알 수 없으므로 비워둬야 한다

model.add(layers.Dense(256, activation='relu', input_shape=(28*28,)))


# 출력층 설정. 숫자의 종류가 10개이므로, 10개의 유닛을 갖는 출력층을 설정한다
# 10개 각각에 대한 확률정보 출력. Softmax 층은 확률 점수를 출력한다
model.add(layers.Dense(10, activation='softmax'))
  • 완젼 연결 층 - Dense 층 (가장 기본이 되는 층)
    • 다 연결하는거 
  • 은닉층에 들어가는 노드의 개수 : 256
  • 첫번째 은닉층 만들 때만 input_shape설정함.
    • 3차원 데이터를 2차원으로 변형할 것. 
  • activation : 활성화 함수를 설정함
    • linear
    • relu
    • sigmoid
    • softmax
  • 출력층 설정할 때, 숫자의 종류가 10개 이므로, 10은 마음대로 조절 불가능 하다. 

 

컴파일 모델

model.compile(loss='categorical_crossentropy', optimizer='rmsprop',metrics=['accuracy'])
print(test_images[1,])
  • loss : 훈련데이터에서 신경망의 성능을 측정하는 손실 함수
  • optimizer : 입력된 데이터와 손실 함수를 기반으로 가중치를 업데이트하는 방법
  • metrics : 훈련과 테스트 과정을 모니터링할 지표 'acc'라고 쓴다. 

 

데이터 변환

train_images = train_images.reshape((60000, 28*28))
train_images = train_images.astype('float32')/255
test_images = test_images.reshape((10000, 28*28))
test_images = test_images.astype('float32')/255
# reshape 함수를 이용해 데이터를 (60000, 784) 크기로 변환하고,
# 각 값을 255로 나누되, type을 소수점을 받는 float32 형으로 맞춤

전처리 후

np.set_printoptions(linewidth=310)
print(test_images[1])

 

 

종속변수를 범주형으로 변환

from tensorflow.keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

 

데이터 학습

#학습
model.fit(train_images, train_labels, epochs=5, batch_size=128)

# 예측
test_loss, test_acc = model.evaluate(test_images, test_labels)
print('test_acc:', test_acc)

313/313 [==============================] - 1s 2ms/step - loss: 0.0722 - accuracy: 0.9801
test_acc: 0.9800999760627747

 

 

문서 행렬 ( DTM )

  • Document Term Matrix
  • 어휘의 빈도수를 파악하고 기록하여 훈련 데이터를 만듦

셋팅

from google.colab import drive
drive.mount('/content/gdrive')

!apt-get update
!apt-get install g++ openjdk-8-jdk

!pip install JPype1
!pip install rhinoMorph

데이터 로딩

def read_data(filename, encoding='cp949'): # 읽기 함수 정의
  with open(filename, 'r', encoding=encoding) as f:
    data = [line.split('\t') for line in f.read().splitlines()]
    data = data[1:] # txt 파일의 헤더(id document label)는 제외하기
  return data

def write_data(data, filename, encoding='cp949'): # 쓰기 함수도 정의
  with open(filename, 'w', encoding=encoding) as f:
    f.write(data)
data = read_data('/content/gdrive/My Drive/pytest/ratings.txt', encoding='cp949')
#data = read_data('ratings.txt', encoding='cp949')

 

훈련 데이터와 테스트데이터 자동 분리

# 훈련데이터와 테스트 데이터 분리 (자동)- 7.5 + 2.5
data_text = [line[1] for line in data]    # 데이터 본문
data_senti = [line[2] for line in data]   # 데이터 긍부정 부분

from sklearn.model_selection import train_test_split  # 본문과 라벨을 각각 분리
train_data_text, test_data_text, train_data_senti, test_data_senti = train_test_split(data_text, data_senti, stratify=data_senti)

# Counter 클래스를 이용해 각 분류가 훈련데이터와 테스트데이터에 같은 비율로 들어갔는지 확인해 본다
from collections import Counter
train_data_senti_freq = Counter(train_data_senti)
print('train_data_senti_freq:', train_data_senti_freq)

test_data_senti_freq = Counter(test_data_senti)
print('test_data_senti_freq:', test_data_senti_freq)

훈련데이터와 테스트 데이터를 분리하는데 70, 30 인지 80,20인지 싸워서 

그냥 자동이면 75% 25%를 사용한다고 한다. 

 

  • 데이터 본문과 긍부정 부분을 분리를 한다. 
  • 본문과 라벨을 각각 분리한 뒤
  • 훈련데이터와 테스트 데이터를 분리한다. 
  • 같은 비율로 들어갔는지 확인. 

train_data_senti_freq: Counter({'0': 186, '1': 184})
test_data_senti_freq: Counter({'1': 62, '0': 62})

 

훈련데이터, 테스트 데이터 수동 분리

# 훈련데이터, 테스트데이터 분리(수동)
import random
random.shuffle(data) # 랜덤하게 섞는다

data_70 = int(len(data)*0.7) # 전체 데이터 크기의 70% 숫자를 찾는다
train_data = data[:data_70] # 앞에서 70% 부분을 잘라 훈련데이터로
test_data = data[data_70:] # 그 뒷부분을 테스트데이터로

print('train data length:', len(train_data)) # 345
print('test data length:', len(test_data)) # 149

# 훈련데이터 요소 분리
train_data_text = [line[1] for line in train_data] # 훈련데이터 본문
train_data_senti = [line[2] for line in train_data] # 훈련데이터 긍부정 부분

# 테스트데이터 요소 분리
test_data_text = [line[1] for line in test_data] # 테스트데이터 본문
test_data_senti = [line[2] for line in test_data] # 테스트데이터 긍부정 부분
  • 먼저 전체 데이터의 70% 찾아서 훈련데이터로
  • 나머지는 테스트 데이터로
  • 훈련데이터와 테스트데이터 요소를 분리한다. 

 

행렬 형태로 변환

from sklearn.feature_extraction.text import CountVectorizer
vect = CountVectorizer(min_df=5).fit(train_data_text) # 최소 문서 빈도 5이상의 단어만 대상
X_train = vect.transform(train_data_text) # 행렬 생성
print("X_train:\n", repr(X_train)) # 생성된 행렬 개요
  • 대장이 되는 데이터가 train_data_text이다. 
  • 하나의 문장에 사용되는 단어가 엉청 많은데
  • 컬럼에 걸릴 확률은 1/100000000
  • 0.05% 채워지는 것은 양호하다.

X_train: <345x71 sparse matrix of type '<class 'numpy.int64'>' with 971 stored elements in Compressed Sparse Row format>

345 X 71 행렬 쌩성

그 중 971 셀에만 데이터 입력

 

vect.transform(train_data_text).toarray()

array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 4, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 1, 0]])

 

feature_names = vect.get_feature_names()
print("특성 개수: ",len(feature_names))
print("\n처음 20개 특성:", feature_names[:20])
print("\n3000~5000까지의 특성:", feature_names[3000:5000])

특성 개수:  71
처음 20개 특성: ['10점', 'ㅋㅋ', 'ㅎㅎ', 'ㅠㅠ', 'ㅡㅡ', '가다', '감독', '감동', '같다', '그냥', '그런', '나오다', '내내', '내용', '너무', '느낌', '다시', '대하다', '되다', '드라마']
3000~5000까지의 특성: []

 

머신러닝 알고리즘 적용

import pandas as pd
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
y_train = pd.Series(train_data_senti) # 리스트 형태를 종속변수가 될 수 있는 1차원 배열(Series)로 만든다
lr = LogisticRegression(solver="liblinear")
lr.fit(X_train, y_train)

-> 모델 생성 및 모델 훈련

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
                   intercept_scaling=1, l1_ratio=None, max_iter=100,
                   multi_class='auto', n_jobs=None, penalty='l2',
                   random_state=None, solver='liblinear', tol=0.0001, verbose=0,
                   warm_start=False)

 

테스트 데이터 입력

X_test = vect.transform(test_data_text)
y_test = pd.Series(test_data_senti)

print("테스트 데이터 점수:", lr.score(X_test, y_test))
  • countVectorizer (새로운 vect만들어서) 안 했다. 
  • test에 대해 만들어진다. 
    • countVectorizer행과 여기서 만들어진 행이 다르다 (행의 길이가 다르면 연산 불가)
    • 우연히 train, test의 열의 수가 같아서 열의 순서가 달라지는 문제가 생긴다. 

테스트 데이터 점수: 0.6912751677852349

 

1개 데이터 예측

# 형태소분석기 시작
import rhinoMorph
rn = rhinoMorph.startRhino()
new_input ='오늘은 정말 재미있는 하루구나!'
inputdata = []
morphed_input = rhinoMorph.onlyMorph_list(
  rn, new_input, pos=['NNG', 'NNP', 'VV', 'VA', 'XR', 'IC', 'MM', 'MAG', 'MAJ'])
morphed_input = ' '.join(morphed_input) # ['오늘', '정말', '재미있', '하루']를 한 개 문자열로 변환

# 공백 조인의 결과는 스트링
inputdata.append(morphed_input) # 분석 결과를 리스트로 만들기
print('input data:', inputdata)

input data: ['오늘 정말 재미있 하루']

# 1개 데이터 예측
  
X_input = vect.transform(inputdata) # 앞에서 만든 11445 컬럼의 행렬에 적용
result = lr.predict(X_input) # 0은 부정,1은 긍정
if result =="0" : # 문자열 형태로 출력된다
  print("부정적인 글입니다")
else:
  print("긍정적인 글입니다")

부정적인 글입니다

감성분석 방법

 

셋팅

from google.colab import drive
drive.mount('/content/gdrive')

#형태소 분석기 설치
!apt-get update
!apt-get install g++ openjdk-8-jdk
!pip install JPype1
!pip install rhinoMorph

 

데이터 읽기, 쓰기 함수 

def read_data(filename, encoding='cp949'): # 읽기 함수 정의
  with open(filename, 'r', encoding=encoding) as f:
    data = [line.split('\t') for line in f.read().splitlines()]
    data = data[1:] # txt 파일의 헤더(id document label)는 제외하기
  return data

def write_data(data, filename, encoding='cp949'): # 쓰기 함수도 정의
  with open(filename, 'w', encoding=encoding) as f:
    f.write(data)
data = read_data('/content/gdrive/My Drive/pytest/ratings.txt', encoding='cp949')
#data = read_data('ratings.txt', encoding='cp949')

 

형태소 분석

import rhinoMorph
rn = rhinoMorph.startRhino()

morphed_data = ''
for data_each in data:
  morphed_data_each = rhinoMorph.onlyMorph_list(rn, data_each[1],
    pos=['NNG', 'NNP', 'VV', 'VA', 'XR', 'IC', 'MM', 'MAG', 'MAJ'])
  joined_data_each = ' '.join(morphed_data_each)    # 문자열을 하나로 연결
  if joined_data_each:                              # 내용이 있는 경우만 저장하게 함
    morphed_data += data_each[0]+"\t"+joined_data_each+"\t"+data_each[2]+"\n"

# 형태소 분석된 파일 저장
write_data(morphed_data, 'ratings_morphed.txt', encoding='cp949')

 

감정 사전 읽기

# 감정사전 읽기
data_id = [line[0] for line in data]    # 데이터 id
data_text = [line[1] for line in data]  # 데이터 본문
data_senti = [line[2] for line in data] # 데이터 긍부정 부분

# 각 줄에 하나의 원소씩 있는데, 리스트 하나에 저장됨
positive = read_data('/content/gdrive/My Drive/pytest/positive.txt') # 긍정 감정사전 읽기
negative = read_data('/content/gdrive/My Drive/pytest/negative.txt') # 부정 감정사전 읽기

print("positive : ",positive)
print("negative : ", negative)

pos_found = []
neg_found = []

 

감정 단어 파악

#하나의 단어를 쪼개서, 그 단어가 있는지 없는 지 파악하게 해준다. 
def cntWordInLine(data, senti):
  senti_found = []
  for onedata in data:
    oneline_word = onedata.split(' ') # 한 줄의 데이터를 공백 단위로 분리하여 리스트로 저장
    senti_temp = 0 # 그 줄에서 발견된 감정단어의 수를 담는 변수
    for sentiword in senti: # 감정사전의 어휘
      
      # 단어가 발견됐는지 안 됐는지만 보기만 하겠다
      if sentiword[0] in oneline_word: # sentiword[0] 하여 리스트 원소를 문자열로 추출
        senti_temp += 1 # 현재의 감정단어와 일치하면 숫자를 하나 올려 줌 (중복X)
    
    senti_found.append(senti_temp) # 현재의 줄에서 찾은 감성단어의 숫자를 해당 위치에 저장
  return senti_found

data_senti_poscnt = cntWordInLine(data_text, positive)
data_senti_negcnt = cntWordInLine(data_text, negative)

print(data_senti_poscnt[:20])
print(data_senti_negcnt[:20])

[5, 1, 0, 0, 2, 1, 0, 0, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 1, 0]
[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1]

 

감정 점수 계산

# pandas 데이터프레임 저장
import pandas as pd
newdata = pd.DataFrame({'id':data_id, 'text':data_text, 'original':data_senti,
                        'pos':data_senti_poscnt, 'neg': data_senti_negcnt})
senti_score = newdata['pos']-newdata['neg']		#금정 개수 - 부정개수
newdata['senti_score'] = senti_score

newdata.loc[newdata.senti_score > 0, 'new'] = 1
# 정답의 데이터가 중립이 없어서 0도 포함
newdata.loc[newdata.senti_score <= 0, 'new'] = 0


# 처음에 기록된 긍부정과 새로 계산된 긍부정 같은지 여부 저장 ( matched 열에)
# to_numeric으로 문자를 숫자로 변환해서 보기
newdata.loc[pd.to_numeric(newdata.original) == newdata.new, 'matched'] = 'True'
newdata.loc[pd.to_numeric(newdata.original) != newdata.new, 'matched'] = 'False'
  • 긍정 개수- 부정개수 빼서 senti_score에 저장함
  • 처음 기록된 긍부정과 새로운 결과랑 비교해서 matched 열에 저장함

 

원 점수와 비교 및 저장

# 벡터 연산 : 같은 위치에 있는 것끼리 같은 연산을 수행
score = newdata.matched.str.count('True').sum() / (newdata.matched.str.count('True').sum()
+ newdata.matched.str.count('False').sum()) * 100
print(score)
newdata.to_csv('newfile.csv', sep=',', encoding='cp949', index=False) # csv 저장
newdata.to_csv('newfile2.txt', sep='\t', encoding='cp949'
, index=False) # 또는 txt 저장

 

 

시그모이드 점수 계산

  • 시그모이드란
    • 모든 값을 0-1로 바꿔줌
    • 정규화하는 효과를 가짐
# 시그모이드 점수 계산
import math
def sigmoid(x):
  return 1 / (1 + math.exp(-x))
newdata['sigmoid'] = newdata.senti_score.apply(sigmoid)

 

 

 

환경 설정

!apt-get update
!apt-get install g++ openjdk-8-jdk
# 2. JPype 설치
!pip install JPype1
# RHINO 설치
!pip install rhinoMorph

 

형태소 분석기 설치

import rhinoMorph
rn = rhinoMorph.startRhino()
#예문 분석
text = "한글 테스트 글을 남겨주세요"
sample_data = rhinoMorph.onlyMorph_list(rn,text)
print('sample data :', sample_data)

sample data : ['한글', '테스트', '글', '을', '남기', '어', '주', '시', '어요']

 

실질 형태소만, 동사 어말어미 제외

# 사용 2 : 실질형태소만, 동사의 어말어미는 제외
# 실제적인 의미가 있는 거 : 
text_analyzed = rhinoMorph.onlyMorph_list(rn, text, pos=['NNG', 'NNP','NP','VV','VA','XR','IC','MM','MAG','MAJ'])
print('\n 2.형태소 분석 결과 :', text_analyzed)

2.형태소 분석 결과 : ['한글', '테스트', '글', '남기']

동사 어말어미 포함

# 사용 3 : 실질형태소만, 동사의 어말어미는 포함
text_analyzed = rhinoMorph.onlyMorph_list(rn, text, pos=['NNG', 'NNP','NP','VV','VA','XR','IC','MM','MAG','MAJ'], eomi = True)
print('\n 3.형태소 분석 결과 :', text_analyzed)

3.형태소 분석 결과 : ['한글', '테스트', '글', '남기다']

 

전체 형태소, 품사정보 가져오기

# 사용 4 
morphs, poses = rhinoMorph.wholeResult_list(rn, text)
print('\n4. 분석 결과\n morphs :', morphs)
print('poses : ',poses)

4. 분석 결과 morphs : ['한글', '테스트', '글', '을', '남기', '어', '주', '시', '어요']

poses : ['NNG', 'NNG', 'NNG', 'JKO', 'VV', 'EC', 'VX', 'EP', 'EF']

 

 

# 원문 어절 정보 같이 가져오기 
text_analyzed = rhinoMorph.wholeResult_text(rn,text)
print('\n5. 형태소 분석 결과 :\n',text_analyzed)

5. 형태소 분석 결과 :

한글  한글/NNG

테스트  테스트/NNG

글을  글/NNG + 을/JKO

남겨주세요   남기/VV + 어/EC + 주/VX + 시/EP + 어요/EF

 

연결된 명사 결합

# 6,7 한 어절에서 연결된 명사를 하나의 명사로 결함
# onlyMorph_list 와 wholeResult_list에서 사용 가능
text_analyzed = rhinoMorph.wholeResult_list(rn, text, pos=['NNG', 'NNP','NP','VV','VA','XR','IC','MM','MAG','MAJ'], combineN = True)
print('형태소 분석 결과 ; \n',text_analyzed)


7. 형태소 분석 결과
morphs :  ['한글', '테스트', '글', '을', '남기', '어', '주', '시', '어요']
poses :  ['NNG', 'NNG', 'NNG', 'JKO', 'VV', 'EC', 'VX', 'EP', 'EF']

 

# 사용 8,9 어근 + 하 형태를 하나의 동사로 출력
# wrVv 아규먼트가 담당하며, 기본값은 False로서 둘을 분리하여 출력함
# 분리된 어근이 명사인 경우, 명사로 출력한다. 
# onlyMorph_list, wholeResult_list , WholeResult_text 등 함수에서 모두 사용 가능

text_analyzed = rhinoMorph.wholeResult_list(rn, '사랑합니다')
print('\n8. 형태소 분석 결과 : ', text_analyzed)

text_analyzed = rhinoMorph.wholeResult_list(rn, '사랑합니다', xrVv = True)
print('\n9. 형태소 분석 결과 : ', text_analyzed)


8. 형태소 분석 결과 :  (['사랑', '하', 'ㅂ니다'], ['XR', 'XSV', 'EF'])

9. 형태소 분석 결과 :  (['사랑하', 'ㅂ니다'], ['VV', 'EF'])

+ Recent posts