ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 패스트캠퍼스 환급챌린지 41일차 미션 (3월 12일) : 딥러닝·인공지능 Signature 초격차 패키지 Online. 강의 후기
    패스트캠퍼스 챌린지 2024. 3. 12. 13:01

     

    자, 오늘은 

     

    CH10-02. 주요코드미리보기(2)

    &

    CH10-03.감정분석모델 실습

     

     

    CH10-02. 주요코드미리보기(2)

    어제에 이어 오늘은 텍스트 데이터 전처리를 알아보도록 하자.

     

     

    텍스트 데이터 전처리란?

    "텍스트를 벡터로 변환하는 작업을 의미한다." 

     

    영문의 경우는 대소문자, 불필요한 단어, 숫자, 따옴표 같은 문장기호도 제거하고 데이터 전처리를 많이 했는데,

    여기에서는 그런게 다 정제된 이후 신경망에 입력하기 위해 변환하는 작업을 말하는 것이다.

     

    텍스트 데이터 전처리 순서

     

    0. 원본 : one reviewer mentioned watching oz episode hooked

     

    1. 사전생성(딕셔너리)

        : 리뷰 문장에 들어있는 단어들을 추출하고, 각 단어에 숫자를 부여하는 작업

           - ['one', 'reviewer', 'mentioned', 'watching', 'oz', 'episode', 'hooked']

     

      분석한 리뷰 데이터를 다 파싱을 해서 단어들을 추출하는 것. 각각의 단어에 숫자를 부여하는 작업을 한다.(띄어쓰기기준)

     

    2. 리뷰 인코딩

        : 리뷰에 포함된 단어를 숫자형태로 변환하는 작업

     
        -  {'i': 1, 'movie': 2, 'film': 3, 'the': 4, 'one': 5, 'like': 6, 'it': 7, 'time': 8, 'this': 9, 'good': 10, 'character': 11,...}    
        - [5, 1095, 972, 74, 2893, 186, 2982, 119, 114, 538]

     

    저렇게 각 단어에 대해 숫자를 부여하는 작업을 하고, 저 맨 위에 one reviewer...하는 저 문장이

    아래와 같은 [5,1095, 972..] 이 숫자로 바뀌게 된다.

    이제 신경망에 넣을 수가 있긴 하다.

     

    3. 길이 맞춰주기: padding or trim

       : 신경망의 입력으로 사용하기 위해 일정 길이만큼 맞춰주는 작업

         - 길이가 긴 문장은 잘라주고(trim), 길이가 짧은 문장은 채워주는(padding) 작업

        - [[  191,  1083,   930,    81,  3724,   186,  3030,     1,   118, 114],<br>
           [   47,   328,    59,   244,     1,     7,  1267,  1608, 17875, 4],<br>
           [    3,    95,   328,    30,  1041,    13,   845,  1774,  2633, 2],...]

     

     길이를 맞춰주는 것이다. 입력으로 들어오는 문장들의 길이가 다 다르기 때문에 긴 문장은 짧아주고, 짧으면 채워주는

    작업을 한다.(입력되는 사이즈가 다 동일해야한다는 말!)

     

    이 데이터를 이제 신경망에 넣어준다.

     

     

    그 다음 알아볼 것은 tqdm이다.

    이 tqdm은 신경망을 학습시킬 때 반복해서 여러번 이터레이션 거쳐 트레인/밸리데이션 수행하는데, 내가 설정한

    에폭중에서 얼마만큼 진행되었는지 프로그레스바와 더불어 진행사항을 표시해주는 기능이다.

     

    그리고 이번 코드에서는 Early stop을 구현한다.

     

    얼리스탑 개념을 다시 복기해보면 여러 에폭을 수행하면서 학습트레인은 성능이 계속 증가하는데,

    validation에서는 로스가 감소하다가 갑자기 증가해버린다.

     

    그래서 에폭을 다하지 않고 에폭을 중간에 정지시키는 것이다.

    오버피팅을 발생하기 때문에 이 얼리스탑을 구현한다.

     

    for e in epochloop:
        train_loss, train_acc = train(model, trainloader) ①
        val_loss, val_acc = validation(model, valloader)
       
        # save model if validation loss decrease
        if val_loss / len(valloader) <= val_loss_min:
            torch.save(model.state_dict(), './sentiment_lstm.pt')
            val_loss_min = val_loss / len(valloader)
            es_trigger = 0
        else:      
            es_trigger += 1

        # early stop
        if es_trigger >= es_limit:
            break

     

    이걸 어떻게 구현하나?!

     

    기존같으면 에폭을 10번을 돌린다! 라고 했을 때 위의 ① 에서 트레인/벨리데이션을 거치면서 어큐러시, 로스값을

    나타낸다.

     

    그러나 진행되는 상황을 계속 모니터링을 할 것이기 때문에, 트레이닝 로스/ 어큐러시를 반환받아야 한다.(val도 포함)

     

    따라서 위에 줄을 보면 for문 안에서 계속 수행되게 되어있는데, 

      if val_loss / len(valloader) <= val_loss_min: (현재상황 저장)

    이번의 validation loss 값이 감소되지 않았을 때, loss가 늘었따는 개념이므로(오버피팅)의 징조...

    그러면 포문을 나오게 설계를 할 것이다.

    es_limit: 이 내가 설정한 리미트값을 넘으면 바로 뛰쳐나가게 된다.

     

    우리는 데이터가 차고 넘치는 세상에서 상황에 맞게 적절한 자료구조를 선택해서 데이터를 아주 자알~

    사용하는 방법을 알고 있어야 한다.

     

     

    CH10-03. 감정분석모델 실습

    자, 거두절미하고 실습을 한번 해보자.

     

    먼저 큰 그림을 그려보면,

     

    1. 라이브러리 가져오기

    2. 데이터로더만들기: 학습용 데이터셋 / 테스트용 데이터셋 분할하고 배치로 나누어서 관리(학습용)

    3. 신경망 구현하기 : LSTM, 임베딩 레이어

    4. 모델 인스턴스 만들기

    5. 모델 컴파일: 학습할때 필요한 옵티마이저, 로스값 설정

    6. 트레인 루프

    7. 테스트 루프

    8. 학습

    이순으로 진행할 것이다.

     

     
    # Step1. Load libraries & Datasets

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns

    import torch
    from torch import nn
    from torch.optim import Adam
    from torch.utils.data import TensorDataset, DataLoader

    import os # 얼리스타핑을 위해 학슴 진행사항을 저장하기 위해 사용하는 패키지
    from tqdm import tqdm
    tqdm.pandas()
    from collections import Counter
     

     

    뭐... tqdm이라든지 이런거 말고는 기존에 알고 있던 것이고,

    os는 얼리스타핑을 위해 학습 진행사항을 저장하기 위해 사용하는 패키지라는데 이따 다시 같이 봐보자.

     

     

    우선....엑설솨이즈 파일을 가져오면 이렇게 뜬다.

     

    레이블은 1, 0 긍정이냐 부정이냐 담겨있다.

     

    전처리가 완료되긴 했는데, 마침표 및 대소문자가 섞여서 있느걸 볼 수 있다. 추가적인 작업을 위해

    프로세스 칼럼에 있는 모든 대문자를 소문자로, 대소문자 a~z 제외 나머지를 다 비어있는 값으로 대체해보자

     

    data['processed'] = data['processed'].str.lower().replace(r"[^a-zA-Z ]", "", regex=True)

     

    프로세스 열에 기존의 프로세스열에 lower(소문자로), relpace(a-z를 제외한 나머지를 공백으로) 이렇게 하면 된다.

     

     

    아주 깔끔하게 만들어줬다.

     

    자 이번에는

     

    2. 사전생성

    이렇게 전처리가 왼료된 데이터를 신경망에 넣기 위해서는 먼저 사전을 생성해야 한다.

    # 사전 생성, 문장에 포함된 단어 토큰화
    reviews = data['processed'].values
    words = ' '.join(reviews).split()
    words[:10]

     

    단어를 파싱해야하는데, 형태소 등 여러방법이 있지만 띄어쓰기 방법으로 단어를 쪼개준다.

    그러면 각각의 단어들이 쪼개져서 리뷰라는 리스트에 담아놓는다.

     

    그 다음 이 단어들에게 숫자들을 매겨줄텐데, 우리는 Count(많이 쓰이는 문자대로 정렬)를 써서 한번 글자를 매겨보자.

     

     

    자 위에 이어서 words 라고 파싱한 단어들을 모아놓은 리스트가 있는데, 

    sort를 이용하여 각 단어가 몇번 나왔는지 파악하고 그다음 정렬을 해준다.

    int2word = dict(enumerate(vocab, 1)) 

    1번부터 숫자를 부여해준다는 뜻이고,

    int2word[0] = '<PAD>'

    제일 마지막 0번에는 패드라는 값을 넣어준다.

     

    리뷰인코딩

     

    one = 5, reviewers = 1085, mentioned = 972 이런식으로 낱말에 각각의 숫자를 인코딩 한걸 알 수 있다.

     

    길이 맞춰주기

    def pad_features(reviews, pad_id, seq_length=128):
        features = np.full((len(reviews), seq_length), pad_id, dtype=int)  # np.full((5, 3), 2)

        for i, row in enumerate(reviews):
            # if seq_length < len(row) then review will be trimmed
            features[i, :len(row)] = np.array(row)[:seq_length]

        return features

    seq_length = 256
    features = pad_features(reviews_enc, pad_id=word2int['<PAD>'], seq_length=seq_length)

    assert len(features) == len(reviews_enc)
    assert len(features[0]) == seq_length

     

    앞서 말한 것처럼 길이를 맞춰줘야 한다.

     

     

    데이터 분할

    # train test split
    train_size = .8
    split_id = int(len(features) * train_size)
    train_x, test_x, train_y, test_y = features[:split_id], features[split_id:], labels[:split_id], labels[split_id:]

    split_id = int(len(train_x) * train_size)
    train_x, valid_x, train_y, valid_y = train_x[:split_id], train_x[split_id:], train_y[:split_id], train_y[split_id:]
    print('Train shape:{}, Valid shape: {}, Test shape: {}'.format(train_x.shape, valid_x.shape, test_x.shape))
    print('Train shape:{}, Valid shape: {}, Test shape: {}'.format(train_y.shape, valid_y.shape, test_y.shape))

     

    train, test 분할 아닌 이번에는 벨리데이션까지 분할하고,

    전체중에 80%를 트레인셋으로 만든다. 

    전처리 완료한 값들이 있는 변수의 전체길이 80%를 스플릿 한다.

     

    또한 트레인 셋의 80프로를 또 트레인과 밸리데이션으로 나눈다.

     

     

    이렇게 배치사이즈까지 따라오면 그 다음은 모델이다.

     

    41일차 끝.

     

     

     

    본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

    https://bit.ly/48sS29N

Designed by Tistory.