728x90
반응형
[ 배운 내용 ]
Chapter 5. CNN 기반 자연어처리
- CNN 개요
- CNN 텍스트 분류
- CNN을 활용한 네이버 영화리뷰 데이터 감성분석
Chapter 5. CNN 기반 자연어처리
CNN (Convolutional Neural Networks)
- 컨볼루션(Convolution) 계층과 풀링(Pooling) 계층들이 순차적으로 쌓여져 있는 Network
- 필터를 사용해서 이미지의 구조를 훼손시키지 않으면서 특징(feature map)을 추출
- 활성화 함수로 비선형 함수 ReLU 사용
- Pooling 계층으로 이미지나 feature map의 크기를 줄여줌으로써 연산량을 줄일 수 있다.
- 신경망을 거친 n개의 클래스 구분 결과를 확률처럼 해석하여 분류하기 위해 Softmax 함수 사용
CNN 기반 텍스트 분류
- CNN을 기반으로 텍스트를 분류할 때는 이미지 분류 문제와 같이 문장의 지역 정보를 보존하면서 각 문장 성분의 등장 정보를 학습에 반영하는 구조이다.
- 학습할 때 각 필터를 조정하면서 언어의 특징 값을 추출하게 되는데 기존의 인접한 단어들을 포함한 문자열로 자르는 N-gram 방식과 유사하다.
- pooling 후 fully connected layer를 사용해서 classification을 수행한다.
- 위의 예시 그림은 7단어(토큰)으로 이루어진 문장의 각 단어를 5차원의 벡터로 표현했다.
- 이 문장을 2단어, 3단어, 4단어로 된 필터 2개를 사용해서 훑어준다.
- 각 필터로 훑은 결과들을 Max Pooling해주고 하나의 벡터로 Concat 해준다.
- 이후 Fully Connected Layer를 거쳐서 분류를 수행해준다.
[ Text CNN의 입력 텍스트 전처리 ]
- 형태소 단위로 토큰화 (MeCab 등의 형태소 분석기를 거쳐서)
- 문법형태로 토큰 제거
[ Text CNN의 하이퍼 파라미터 ]
- 단어 임베딩벡터 크기 (d) : 단어의 정보를 담고 있는 벡터의 차원
- ex) d=5
- 분류 클래스 개수
- ex) 정/부정 2개
- 사용할 커널 (필터) 종류
- ex) [2, 3, 4] ==> 2단어, 3단어, 4단어짜리 필터
- 각 커널에는 처음에는 랜덤한 값을 부여한다.
[ Text CNN 과정 ]
- Text를 임베딩 벡터로 변환
m = nn.Embedding(1000,100)
- Convolution Layer 통과
input = torch.randn(20, 1, 28, 28) # mini_batch=20
# (데이터건수, 채널수, 높이, 너비)
m == nn.Conv2d(1, 16, 5, stride=2)
# (인풋채널, 아웃풋채널(필터개수), kernel_size, stride)
- 텍스트는 채널 정보가 없기 때문에 Conv2d를 사용하려면 unsqueeze로 채널 정보를 추가해주어야 한다.
input = torch.randn(20, 100, 100) # batch=20
input = input.unsqueeze(1) # (20 x 1 x 100 x 100)
# Conv2d(in_channels, out_channels, kernel_size, stride=1)
# 필터(kernel)은 3x100
m = nn.Conv2d(1, 100, (3,100))
output = m(input)
# output 텐서의 차원 20 x 100 x 98x1
# 20 : 배치
# 100 : filters (필터개수 == conv출력개수)
# 98x1 : 100x100에 3x100필터 훑은 결과
- 활성화 함수와 pooling까지 적용
input = torch.randn(20, 100, 100) # batch=20
input = input.unsqueeze(1) # (20 x 1 x 100 x 100)
m1 = nn.Conv2d(1, 100, (3,100)) # 필터개수 100개
m2 = nn.Conv2d(1, 100, (4,100)) # 필터개수 100개
m3 = nn.Conv2d(1, 100, (5,100)) # 필터개수 100개
output1 = m1(input) # 20 x 100 x 98 x 1
output2 = m2(input) # 20 x 100 x 97 x 1
output3 = m3(input) # 20 x 100 x 96 x 1
output1 = F.relu(output1) # 20 x 100 x 98 x 1
output2 = F.relu(output2)
output3 = F.relu(output3)
p1 = nn.MaxPool1d(output1.size(2)) # p1 = F.max_pool1d(output1, output1.size(2)) 도 가능
p2 = nn.MaxPool1d(output2.size(2))
p3 = nn.MaxPool1d(output3.size(2))
# Max Pooling 결과
# MaxPool1d는 3D 입력만 받기 때문에 squeeze(3) --> 20 x 100 x 98
output1 = p1(output1.squeeze(3)) # 20 x 100 x 1 : max pooling으로 98개중 1개만 남음
output2 = p1(output2.squeeze(3)) # 20 x 100 x 1
output3 = p1(output3.squeeze(3)) # 20 x 100 x 1
# Concatenate
output1 = output1.squeeze(2) # 20 x 100
output2 = output2.squeeze(2) # 20 x 100
output3 = output3.squeeze(2) # 20 x 100
output = torch.cat( (output1, output2, output3), 1 ) # 300개
fc = nn.Linear(300, C) # (입력채널크기, 출력채널크기) C : 분류 클래스 개수
out = fc(x)
[ CNN을 활용한 NSMC 네이버 영화리뷰 데이터 감성분석 실습 ]
NSMC 데이터셋
- 네이버 영화 리뷰 크롤링 데이터 사용
- 평가 9 ~ 10(긍정), 1 ~ 4(부정)
- 긍/부정 분석이기 때문에 나머지 점수는 학습데이터에서 제외
torchtext.legacy
- text를 위한 데이터 로더를 제공
- 토크나이징, 단어 사전 생성, 토큰의 수치화, 데이터로더 생성과 같은 기능들을 편리하게 지원
# torchtext.legacy를 사용할 수 있는 torchtext 버전 설치
!pip install -U torchtext==0.10.0
- 임포트
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable # 미분을 위한 함수들이 정의되어 있음
from torchtext.legacy import data
import torchtext.datasets as datasets
import pickle
[ Text CNN 모델 구조 정의 ]
하이퍼 파라미터
- V : 사전의 크기
- D : embed_dim (단어벡터 차원)
- C : 분류하려는 클래스 개수
- Co : output channel 수 (필터의 개수)
- Ks : 필터 종류
Conv2d 계층
- 하나의 컨볼루션 계층에 필터 종류 만큼의 컨볼루션 모듈의 리스트가 들어간다. ( nn.ModuleList() )
- forward에서 순차적으로 접근
Linear 계층
- Conv계층을 거친 결과가 입력으로 들어오는데 각 필터 종류마다 컨볼루션을 거친 결과가 Co개 있으므로 len(Ks)*Co 크기의 인풋이 들어온다.
- C개의 클래스를 분류하는 문제
forward() 함수
- 모델이 학습데이터를 입력받아서 forward 연산을 진행한다.
- model 객체를 데이터와 함께 호출하면 자동으로 실행된다.
- self.embed(x) : 입력 x는 (N, W, D) 의 shape를 갖는다. (미니배치, 문장 최대길이, 단어벡터 차원)
- torch.nn은 미니배치 형태의 입력만 지원하기 때문에 Conv2d 계층의 인풋은 배치 차원이 추가된 4D 형식이어야 한다.
- x.unsqueeze(1) : Conv2d를 사용하려면 4차원이 되도록(N x Ci x W x D) 입력채널의 수를 추가해야 한다. (미니배치, 채널 인풋, 문장 최대길이, 단어벡터 차원)
- 텍스트는 이미지 데이터와 다르게 차원이 하나 적기 때문에 텐서에 dimension 1의 차원을 인위적으로 추가해준다. (0, 1, 2, 3) 차원 순서
- max_pool1d에서는 3D입력을 받기 때문에 conv레이어를 거친 후에는 squeeze(3)으로 텐서의 dimension 3을 squeeze 해준다.
- relu를 거치고 나면 dimension 3 차원의 값은 1을 갖기 때문에 squeeze
class CNN_Text(nn.Module):
def __init__(self, embed_num, class_num):
super(CNN_Text, self).__init__() # nn.Module 클래스를 초기화
V = embed_num # 사전의 크기 (== 임베딩 벡터의 개수 == 단어의 개수)
D = 100 # embed_dim (단어벡터 차원) 모든 단어는 100차원의 벡터로 표현됨
C = class_num # 분류하려는 클래스 개수 (감성분석 -> 2개)
Co = 50 # output channel 수 (50개의 필터로 훑어서 50개의 아웃푹)
Ks = [2,3,4] # 필터 종류 (2단어, 3단어, 4단어 짜리 필터)
# 사전에 있는 모든 단어 임베딩 벡터에 random 초기값
self.embed = nn.Embedding(V, D)
# torch.nn.Conv2d (in_channels, out_channels, kernel_size, stride=1)
self.convs1 = nn.ModuleList([nn.Conv2d(1, Co, (K, 100)) for K in Ks]) # Ks가 2,3,4일때 별로 컨볼루션 레이어를 순차적으로 접근
self.dropout = nn.Dropout(0.2) # 드롭아웃
# Fully Connected Layer
# torch.nn.Linear(in_features, out_features)
self.fc1 = nn.Linear(len(Ks)*Co, C)
def forward(self, x):
x = self.embed(x) # (N, W, D) 미니배치, 문장 최대길이, 단어벡터 차원
x = x.unsqueeze(1) # (N x Ci x W x D) 입력채널 수 추가
# Convolution Layer
# Convolution -> ReLU -> 텐서의 dimension 3을 squeeze(max_pool1d는 3D 입력을 받음)
x = [F.relu(conv(x)).squeeze(3) for conv in self.convs1] # 모듈 리스트를 루프를 돌려서 각 컨볼루션에 대해 작업
# Max Pooling
# F.max_pool1d(input, kernel_size): Applies a 1D max pooling over an input
# Tensor.size(dim=None) : Returns the size of the self tensor. If dim is specified, returns the size of that dimension.
x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x] # [(N, Co), ...]*len(Ks) max pooling 후에 마지막 차원은 1 -> squeeze
x = torch.cat(x, 1) # torch.cat(tensors, dim), dim=1이면 두번째 차원이 늘어나게 concat (첫번째 차원은 N)
x = self.dropout(x) # (N, len(Ks)*Co), dropout을 적용
logit = self.fc1(x) # fully-connected layer 적용
return logit
- 주석 빼고 보기
class CNN_Text(nn.Module):
def __init__(self, embed_num, class_num):
super(CNN_Text, self).__init__()
V = embed_num
D = 100
C = class_num
Co = 50
Ks = [2,3,4]
self.embed = nn.Embedding(V, D)
self.convs1 = nn.ModuleList([nn.Conv2d(1, Co, (K, 100)) for K in Ks])
self.dropout = nn.Dropout(0.2)
self.fc1 = nn.Linear(len(Ks)*Co, C)
def forward(self, x):
x = self.embed(x) # (N, W, D)
x = x.unsqueeze(1) # (N x Ci x W x D)
x = [F.relu(conv(x)).squeeze(3) for conv in self.convs1] # [(N, Co, W), ...]*len(Ks)
x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x] # [(N, Co), ...]*len(Ks)
x = torch.cat(x, 1)
x = self.dropout(x) # (N, len(Ks)*Co)
logit = self.fc1(x) # (N, C)
return logit
이후 과정은 DAY3 소스코드 참고하기
정리 잘돼있는곳
728x90
반응형
'KT AIVLE School' 카테고리의 다른 글
(16주차 - 22.11.02~22.11.03) 가상화 클라우드1 - AWS (0) | 2022.11.03 |
---|---|
(16주차 - 22.10.31~22.11.01) IT인프라 - 가상환경, 리눅스 명령어 (0) | 2022.11.01 |
(12주차 - 22.10.04) 언어지능딥러닝2 - 텍스트 마이닝, 워드 임베딩 (0) | 2022.10.05 |
(12주차 - 22.09.30) 언어지능딥러닝1 - 자연어처리 기술, 기계학습 기반 자연어처리 (0) | 2022.10.01 |
(10주차 - 22.09.22-23) 시각지능딥러닝3 - 커스텀 이미지 데이터 Detection하기 + Annotation (0) | 2022.09.22 |