[참고] 본 포스팅은 수리링 본인이 Attention is all you need 논문을 처음부터 끝까지 직접 읽으며 분석하고 리뷰하여 작성했습니다. 불펌 절대 금지! 본문 내용에 잘못된 부분이 있다면 댓글 달아주세요.


Transformer

Transformer

이전 포스팅에서 다루었던 LSTM과 GRU와 같은 새로운 모델들은 기존 RNN 모델의 Long-term Dependency, Exploding Gradient 문제를 해결하기 위해 고안되었었죠. 하지만 안타깝게도 근본적인 문제가 완전히 해결된 것은 아니었다고 해요. 그 이유는 RNN이나 CNN이 가지는 연쇄적인 계산구조 때문이었는데요. 따라서 Recurrent 구조가 아닌 새로운 구조의 모델로 Sequence Data를 다루고자 하는 시도가 계속되었다고 합니다. 그리고 2015년, 인공지능 역사에 한 획을 긋는 Attention이라는 개념이 새롭게 도입됩니다.

You got me looking for attention...?

간단하게 말하면, 어텐션이란 모든 기억을 동등하게 기억하지 않고 연관성 있는 기억에 집중해서 기억하도록 구조화하는 기법을 말합니다. 어텐션이 정말 획기적인 메커니즘이긴 했지만, 초기에는 RNN, CNN 구조와 함께 사용되었기 때문에 여전히 시퀀스의 길이가 길어질수록 같은 문제가 발생하는 한계가 있었는데요. 2017년 구글은 Attention is all you need이라는 정말 멋있는,,, 제목으로 Transformer (트랜스포머) 모델을 제안합니다. 트랜스포머 모델은 RNN, CNN 구조를 완전히 배제하고 오롯이 어텐션 그 잡채! 에만 집중하는 아이디어로 기존의 문제점을 기냥,, 해결해버렸습니다.

본 포스팅에서는 트랜스포머 논문을 처음부터 끝까지 직접! 읽으며 분석한 내용을 정리하고 공유하고자 합니다 :)

논문 출처 :  https://arxiv.org/pdf/1706.03762


초록 Abstract

먼저 논문의 전체적인 아이디어와 내용을 파악할 수 있는 초록을 읽어 보도록 하겠습니다.

The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely.

기존 모델들의 인코더-디코더에는 복잡한 RNN 구조나 CNN 구조가 포함되어 있는데, 우리는 RNN/CNN구조를 완전히 배제하고 온전히 어텐션 메커니즘에만 기반한 새로운 네트워크 구조 '트랜스포머'를 제안한다! 라고 되어 있습니다. Attention is all you need이라는 논문 제목도 그렇고, Transformer이라는 모델 이름도 그렇고, 너무 멋있어서 소름이 돋습니다.

We show that the Transformer generalizes well to other tasks by applying it successfully to English constituency parsing both with large and limited training data.

트레이닝 데이터가 많든지 적든지간에 관계없이 트랜스포머가 영어 구문 분석에 굉장히 성공적으로 적용이 되었는데, 영어 구문 분석 뿐만 아니라 다른 과업에도 트랜스포머가 잘 일반화되어 적용될 수 있다는 것을 보여주겠다고 합니다.

이제 서론을 봅시다.



서론 Introduction

Recurrent neural networks, long short-term memory [13] and gated recurrent [7] neural networks in particular, have been firmly established as state of the art approaches in sequence modeling and transduction problems such as language modeling and machine translation [35, 2, 5]. 

RNN, LSTM, GRU같은 모델들은 언어모델이나 번역기같은 sequence modeling과 transduction problems에 특히 잘 적용되어 왔는데요.

they generate a sequence of hidden states ht, as a function of the previous hidden state ht−1 and the input for position t. This inherently sequential nature precludes parallelization within training examples, which becomes critical at longer sequence lengths, as memory constraints limit batching across examples. ...... The fundamental constraint of sequential computation, however, remains.

이런 RNN 구조의 모델들은 시퀀스가 길어질수록  어쩔 수 없이 맛탱이가 가버렸고..... 이런 문제를 해결하기 위해 factiorization trick이나 conditional computation같은 방법이 고안되기도 했지만, 결국 Recurrent 구조는 그대로였기때문에 근본적인 문제점은 여전히 해결되지 못한 상태였습니다.

Attention mechanisms have become an integral part of compelling sequence modeling and transduction models in various tasks, allowing modeling of dependencies without regard to their distance in the input or output sequences [2, 19]. In all but a few cases [27], however, such attention mechanisms are used in conjunction with a recurrent network.

이때 혜성처럼 등장한 어텐션 메커니즘은 서로간의 dependency를 계산하면서 입출력 시퀀스의 길이가 길어져도 학습이 잘 되도록 도왔는데요. 안타깝게도 문제점의 근원이 되는 Recurrent 구조가 여전히 그대로 사용이 되었기 때문에, 상황이 좀 나아지긴 했으나, 시퀀스 길이가 길어지면 또 같은 문제점이 발생했다고 합니다.... 이쯤 되니 '이거 그냥 Recurrent 구조 자체를 쓰지 말아야 겠다'는 생각을 슬슬 시작했던 것 같습니다.

In this work we propose the Transformer, a model architecture eschewing recurrence and instead relying entirely on an attention mechanism to draw global dependencies between input and output. ...

그래서 구글이 제안하는 트랜스포머는 입출력 사이의 Global dependency를 계산할 수 있도록 오롯이 어텐션 메커니즘에만 집중하고 Recurrence 구조를 완전히 배제한다고 합니다. 그렇게 했더니.. 학습도 잘되고... 성능이.. 쩐다고 하네요!



연구 배경 Background

연구 배경에서는 다양한 이전 모델들에 대해서 거론하고 있습니다.

언급된 주요 모델 중 4가지에 대해 찾아보았는데요.

  • [9] ConvS2S (2017): CNN 구조로 시퀀스 간의 종속성을 학습, 병렬 처리를 통해 효율성을 높인 모델
  • [16] Extended Neural GPU (2016): CNN 구조로 긴 시퀀스를 병렬로 처리하는 데 중점
  • [17] Neural GPU (2016): CNN 구조로 알고리즘을 학습하고 긴 시퀀스 데이터를 병렬로 처리할 수 있도록 설계
  • [18] ByteNet (2016): 깊고 확장 가능한 CNN 구조로 입력 시퀀스와 출력 시퀀스 간의 종속성을 효과적으로 학습

대부분 어텐션을 적용하지 않은 모델들이었습니다. 이 밖에도 대체로 RNN, CNN 구조를 사용한 기존 모델들에 대해서 언급을 하면서, 우리가 걔네보다 월등하다는 걸 증명하겠다!는 이야기를 하고 있습니다.

To the best of our knowledge, however, the Transformer is the first transduction model relying entirely on self-attention to compute representations of its input and output without using sequencealigned RNNs or convolution.

마지막으로 현재까지(발표 시점까지) 트랜스포머가 입출력 사이의 관계를 규명하기위해 self-attention 계산에만 온전히 집중하며, 연쇄적인 RNN이나 CNN 구조를 전혀 사용하지 않는 유일한! 모델이라고 거듭 강조하고 있습니다.

그럼 이제 본격적으로 모델 구조에 대해서 알아보겠습니다.



모델 구조 Model Architecture

관심있는 분이라면 한번쯤은 보셨을 트랜스포머의 구조도입니다. 처음 봤을때 저는 이게 도대체 뭘까 굉장히 심란했었는데요, 관련 자료를 열심히 찾아 읽고 논문도 계속 뒤져보고 하면서 한 번 이해하고 나니, 생각보다 심플한 구조구나- 느껴져 재밌었습니다.


3-1. 인코더 - 디코더

먼저 왼쪽 구조도는 인코더, 오른쪽 구조도는 디코더에 해당합니다. 구조 양옆에 N* 이라고 표시된 부분은 해당 작업을 N번 반복하겠다는 것을 의미합니다. 논문에서는 인코더와 디코더에서 모두 N = 6을 사용한다고 밝히고 있습니다.

  • 인코더 레이어는 2개의 하위 레이어로 구성되어 있습니다. 첫 번째 레이어는 멀티헤드 어텐션을 수행하며, 두 번째 레이어는 간단한 Feed-Forward 구조로 이루어져 있습니다. 두 하위 레이어는 ResNet에서 제안했던 잔차연결(Residual connection)으로 연결되는데, 각 하위 레이어의 아웃풋은 인풋과 더해진 뒤(add) 정규화(Norm)됩니다.
  • 이런 더하기 작업은 모든 레이어의 아웃풋이 같은 차원으로 전부 통일되기 때문에 가능합니다.
  • 모든 결과물의 차원을 논문에서는 512로 고정하고 있습니다. 차차 알아보겠지만 레이어 안팎으로 벡터끼리 서로 계속 더하는 일이 많기때문에, 모든 벡터의 shape과 차원을 동일하게 통일할 필요가 있습니다.

  • 디코더는 3개의 하위 레이어로 구성되어 있는데, 인코더와 마찬가지로 모든 하위 레이어는 Residual connection으로 연결됩니다.
  • 특히 첫 번째 하위 레이어에서 Masked 멀티헤드 어텐션을 사용하는 이유 cheating을 방지하기 위함입니다. 해당 내용은 뒤에서 더 자세히 살펴보도록 하겠습니다.

3-2. 어텐션

An attention function can be described as mapping a query and a set of key-value pairs to an output, where the query, keys, values, and output are all vectors. 

어텐션을 하나의 함수의 관점에서 생각해봅시다. 함수는 입력을 받아 결과를 출력하잖아요? 어텐션 함수는 인풋으로 3가지를 받아요. 바로 query, key, value인데요. 이렇게 3가지 인풋을 받아서 맵핑이란 걸 해가지고 결과물을 출력(output)합니다. 인풋으로 사용된 query(Q), key(K), value(V)와 출력물 output은 전부 벡터 형태로 이루어져 있습니다.

여기서부터 저는 이런 의문이 들었습니다. 그래서 Q는 뭐고, K는 뭐고, V는 뭔데? 의문을 해결하기 위해 잠깐 순서를 건너뛰고 3-2 대신 3-5를 먼저 보겠습니다.


3-5. 포지셔널 인코딩 (Positional Encoding)

포지셔널 인코딩을 먼저 이해하면 좋습니다. 간단하게 Input은 'I am hungry'이라는 영어 문장이고, Output은 '나는 배가 고프다'라는 한국어 문장이라고 생각해 봅시다. 논문 초록과 서론에서 누누이 봤지만, 트랜스포머는 이 문장들을 순차적으로 입력받지 않잖아요. 그렇다면 트랜스포머는 이 정보들을 도대체 무슨 수로 sequence data처럼 핸들링할 수 있는 걸까요? 그걸 가능하게 해주는 것이 바로 포지셔널 인코딩입니다.

Since our model contains no recurrence and no convolution, in order for the model to make use of the order of the sequence, we must inject some information about the relative or absolute position of the tokens in the sequence. To this end, we add "positional encodings" to the input embeddings at the bottoms of the encoder and decoder stacks. The positional encodings have the same dimension dmodel as the embeddings, so that the two can be summed. There are many choices of positional encodings, learned and fixed [9].

영어든 한국어든 단어로 된 문장을 받았다면, 기본적인 임베딩 작업을 해서 문장을 벡터화 해야겠죠. 포지셔널 인코딩은 쉽게 말해 이 임베딩 벡터값에 '위치(position) 정보'를 더해주는 일입니다. 위의 구조도에 잘 보면 Input Enbedding과 Positional Encoding 사이에 더하기(+) 기호가 있지요. 물론 두 벡터를 더해줘야 하므로 인풋 임베딩과 포지셔널 인코딩의 shape는 당연히 같도록 설계되었습니다. 어쨌든 이렇게 두 정보를 더함으로써 인코더와 디코더에 위치 정보가 더해진 벡터가 제공되고, 덕분에 마치 유사 sequence data처럼 인식을 할수 있게 되는 겁니다. 또, 이렇게 완성된 벡터에 각각 서로 다른 weight를 걸어서 어텐션 레이어의 서로 다른 입력 Q, K, V가 되는 것입니다. 어떤 weight를 걸어야 최적일지 찾는 건 결국 컴퓨터가 우리에게 해주는 일이 되겠죠!

포지셔널 인코딩을 하는 방법은 다양한데, 트랜스포머는 위의 식과 같은 삼각함수 형태의 식을 취하고 있습니다. 임베딩 벡터 내의 각 차원의 인덱스가 짝수인 경우에는 사인함수의 값을 사용하고, 홀수인 경우에는 코사인 함수의 값을 활용합니다. 이렇게 해야만 하는 건 아니고, 이렇게 했더니 계산과 학습이 용이하면서 성능도 잘 나오더라! 정도로 나름 간단하게 설명을 하고 있습니다. 

https://velog.io/@gibonki77/DLmathPE

포지셔널 인코딩에 대해서 좀더 직관적인 이해가 필요하시면, 위의 포스팅을 참고하시는 것을 추천합니다. 정리가 엄청 잘돼있습니다.


3-2. 어텐션

그럼 다시 Attention 설명파트로 돌아오겠습니다. 먼저 우리가 기존의 전체 구조도에서 살펴본 멀티헤드 어텐션이란 'Scaled Dot-Product Attention'이라는 걸 여러 번 실행하는 것을 의미하는데요. 굉장히 직관적인 작명을 해서... 크기를 줄이는 Scale과 Dot-product(행렬곱 내적)이 사용되는 싱글 어텐션이라고 생각을 하면 됩니다. 

  • 먼저 Q와 K를 행렬곱 계산합니다. Q와 K는 shape이 같은 벡터입니다. 따라서 벡터 내작 계산을 하기 위해 K를 Transpose 합니다. 따라서 두 행렬을 내적한 결과로 행렬은 정방형이 됩니다. (shape이 (a, b)라고 하면, 결과는 (a, a))
  • 그 다음 벡터 안의 숫자값을 작게 줄이는 Scaling 작업을 해줍니다. 논문에서는 벡터 안의 숫자값을 작게 해줌으로써 vanishing gradient 문제를 방지할 수 있게 된다고 밝히고 있습니다.
  • 만약 디코더라면 치팅 방지를 위해 Mask 작업을 추가합니다.
  • 이제 거기다가 Softmax 함수를 걸어주면, 벡터 안의 값이 확률화 되면서 weigt metrix로 간주할 수 있게 됩니다.
  • 그렇게 해서 나온 결과 벡터를 마지막으로 V와 행렬곱 계산합니다. 이 때, 결과 벡터의 shape은 기존 Q, K, V와 동일하게 유지됩니다.((a,a)•(a,b)=(a,b)) 여기까지가 바로 'Scaled Dot-Product Attention' 싱글 어텐션입니다.

  • 트랜스포머는 멀티 헤드 어텐션을 이용합니다. 위에서 살펴본 'Scaled Dot-Product Attention' 형태의 싱글헤드 어텐션을 h번 각각 수행하여 concat해서 이어 붙여준다는 건데요. 예를 들어서 논문에서는 512차원의 벡터를 다루고 있으므로, 이걸 64차원씩 8세트로 나누어서 각각 어텐션을 따로 수행하고 나서 Concat, 즉 이어 붙여서 다시 512차원으로 만들어 준다는 거예요.
Multi-head attention allows the model to jointly attend to information from different representation subspaces at different positions. With a single attention head, averaging inhibits this.
  • 멀티헤드 어텐션은 트랜스포머 모델의 성능을 높이는 핵심 요소 중 하나인데요. 그 이유를 살펴보겠습니다.
  • 먼저 Single head 어텐션은 하나의 문장에서 각 단어의 중요도를 계산한 후, 이 중요도를 이용해 모든 단어의 정보를 결합하여 하나의 벡터를 출력합니다. 이 과정에서 다양한 단어의 정보가 섞이게 되어 특정 단어의 중요한 세부 정보가 희석될 수 있는 가능성이 있고, 논문에서는 이것을 'averaging'이라고 부르고 있습니다. 중요한 패턴이나 특이점이 평균화되면서 사라지게 된다는 것이죠.
  • 반면 Multi head 어텐션은 여러 개의 어텐션 헤드를 사용하여 각 헤드가 특정 위치나 특징에 더 집중할 수 있게 돕고, 다양한 정보를 더 풍부하게 학습할 수 있게 됩니다. 즉 다양한 관점에서 입력 데이터를 이해하고 처리할 수 있게 되는 건데요. 이를 통해 모델은 입력 데이터의 여러 측면을 동시에 학습하고, 중요한 정보를 놓치지 않게 됩니다. 복잡하고 다양한 패턴을 효과적으로 인식하고 처리할 수 있을 뿐만 아니라 모델의 표현력도 향상시키는 거죠. 그래서 멀티헤드 어텐션이 트랜스포머 모델의 성능을 크게 향상시키는 핵심 요소라는 겁니다.

트랜스포머에서 어텐션은 크게 3곳에서 적용됩니다.

The encoder contains self-attention layers. In a self-attention layer all of the keys, values and queries come from the same place, in this case, the output of the previous layer in the encoder. 
  • 먼저 인코더는 self-attention layer를 사용하는데, 위에서 살펴봤듯이 인코더에 맨 처음 들어오는 입력값은 임베딩과 포지셔널 인코딩된 값이 더해진 벡터입니다. 이 벡터에 각각 서로 다른 weight가 걸려서 Q, K, V라는 입력값으로 들어오게 되지만, 실제로는 전부 같은 값을 가진 하나의 벡터로부터 근원합니다. 논문에서도 'keys, values and queries come from the same place'라고 밝히고 있죠. 
Similarly, self-attention layers in the decoder allow each position in the decoder to attend to all positions in the decoder up to and including that position. We need to prevent leftward information flow in the decoder to preserve the auto-regressive property. We implement this inside of scaled dot-product attention by masking out (setting to −∞) all values in the input of the softmax which correspond to illegal connections. See Figure 2.
  • 디코더에서도 똑같이 self-attention layer를 사용합니다. output에 해당하는 문장을 임베딩한 값과 포지셔널 임베딩한 값을 더한 벡터에 서로 다른 weight가 걸려 Q, K, V가 되고, Masked 작업을 추가한 멀티헤드 어텐션을 수행하죠. 그림의 2번에 해당합니다.
  • 우리가 마스크를 쓰면 마스크 아래 얼굴이 가려져서 보이지 않는 것처럼, 마스킹 작업은 어떤 값이 보이지 않게 가려버리는 것을 말합니다. 여기서는 벡터에서 마스킹할 부분을 마이너스 무한대로 발산하는 -inf값으로 바꿔버리는데, 그러면 이 -inf값이 다음 단계의 softmax 함수를 지나면서 0값을 가지게 됩니다.

참조

  • 기존의 RNN 모델들은 시퀀스가 순차적으로 입력되기 때문에, 앞쪽부터 순차적으로 업데이트 되어온 hidden state를 다음 시퀀스에 제공하면서 sequence 예측을 합니다. 하지만 트랜스포머 모델은 데이터가 순차적으로 제공되지 않고 한번에 제공됩니다. 따라서 현재 시점보다 뒤에 올 시퀀스의 정보까지 알 수 있게, 즉 'cheating'을 할 수 있게 되는 거죠.
  • 논문은 디코더에서 leftward information flow를 방지해야 한다고 언급합니다. 마스킹은 참조용 그림에서 보이는 것처럼 벡터의 대각선 왼쪽 아래 부분을 아예 0값으로 만들어버리고, 그래서 현재 시점보다 뒤에 있는 시퀀스를 참조하지 않도록 돕습니다. 쉽게 말하자면 현재 토큰보다 나중에 해당하는 토큰 값이 계산에 포함되는 부분을 모두 가려버린 것입니다.
In "encoder-decoder attention" layers, the queries come from the previous decoder layer, and the memory keys and values come from the output of the encoder. This allows every position in the decoder to attend over all positions in the input sequence. This mimics the typical encoder-decoder attention mechanisms in sequence-to-sequence models such as [38, 2, 9].
  • 마지막으로 그림 3번에 해당하는 어텐션입니다. 이 어텐션에도 Q, K, V가 필요하겠죠.
  • 먼저 디코더의 첫 번째 하위레이어를 지난 결과물이 Q가 됩니다. (queries come from the previous decoder layer) 그리고 인코더의 최종 결과물에 서로 다른 weight가 걸려서 각각 K, V,가 되고요. 이 결합 메커니즘을 통해 디코더의 각 위치가 입력 시퀀스의 모든 위치를 참조할 수 있게 됩니다.
  • 즉, 디코더의 특정 위치에서 다음 토큰을 예측할 때, 입력 시퀀스 전체의 정보를 활용할 수 있게 되는 것입니다. 이는 시퀀스-투-시퀀스(sequence-to-sequence) 모델에서 일반적으로 사용되는 인코더-디코더 어텐션 메커니즘과 유사한데요. 덕분에 디코더가 보다 정확하고 문맥에 맞는 출력을 생성할 수 있겠습니다.

3-3. Feed-Forward 네트워크

이전에 살펴본 바와 같이 인코더와 디코더는 각각 2개, 3개의 하위 레이어로 구성되어 있는데, 마지막 하위 레이어는 feed-forward 네트워크로 구성되어 있으며, 활성함수로는 ReLU가 사용됩니다. 레이어마다 사용된 웨이트는 모두 다르다고 밝히고 있습니다.


3-4. 임베딩 & 소프트맥스

  • input과 output 토큰을 벡터화하기 위해 learned embedding을 사용하는데, 이 임베딩에 필요한 weight matrix는 같은 것을 쓴다고 밝히고 있습니다. 또한 디코더 결과물을 선형 변환하는 과정에서도 같은 weight matrix를 쓴다고 합니다. 즉, 아래 그림에 빨간색으로 표시한 3곳에서 모두 같은 가중치 행렬을 사용합니다.

  • 디코더의 마지막 feed-forward를 지난 결과물은 linear transformation과 softmax를 차례대로 거쳐 다음 토큰의 probabilities를 예측하는 아웃풋이 됩니다.

3-5. 포지셔널 인코딩

위에서 미리 봤으므로 생략



Why self attention

4장에서는 Self-attention의 장점을 recurrent, convolution 구조와 비교하여 크게 3가지 측면에서 비교합니다.

One is the total computational complexity per layer. Another is the amount of computation that can be parallelized, as measured by the minimum number of sequential operations required. The third is the path length between long-range dependencies in the network.
  1. 레이어마다 발생하는 계산의 복잡도가 상대적으로 낮아 효율적입니다.
  2. 병렬화가 용이해 동시에 많은 계산을 수행할 수 있습니다.
  3. 입력과 출력 위치 간의 경로 길이가 짧아 장기 의존성을 더 쉽게 학습할 수 있습니다.
Learning long-range dependencies is a key challenge in many sequence transduction tasks. One key factor affecting the ability to learn such dependencies is the length of the paths forward and backward signals have to traverse in the network. The shorter these paths between any combination of positions in the input and output sequences, the easier it is to learn long-range dependencies [12]. Hence we also compare the maximum path length between any two input and output positions in networks composed of the different layer types.

n이 시퀀스의 길이, d가 차원의 크기, k가 convolution에서 커널의 사이즈, r이 길이가 제한된 self-attention에서 이웃의 사이즈를 의미할 때 계산의 복잡도를 비교한 표입니다. Self-attention 레이어는 모든 위치를 일정한 수의 순차적 연산으로 병렬 연결할 수 있기 때문에 Recurrent, Convolution 구조보다 훨씬 적은 비용으로 계산을 처리할 수 있습니다.

특히 길이가 제한된 self-attention이란, 입력 시퀀스의 각 위치가 특정 범위 내의 이웃 위치들만 제한적으로 참조하여 어텐션을 계산하는 것을 의미합니다. 예를 들어 시퀀스 길이 n = 100이고 길이 제한 범위 r = 5인 경우, 일반적인 self-attention에서는 각 위치가 99개의 다른 위치와 전부 상호 작용하지만, 제한된 self-attention에서는 각 위치가 최대 10개의 이웃 위치(왼쪽 5개, 오른쪽 5개)와만 상호 작용합니다. 딱 봐도 계산량이 확 줄어서 효율적인 장점이 있겠죠. 특히 입력 시퀀스의 길이가 매우 길고 계산 자원이 제한된 상황에서 유리할 수 있다고 합니다.

추가적으로 self-attention을 사용한 모델은 해석이 조금 더 용이한 장점이 있다고 합니다.



학습과 결과 Training / Results

학습과 결과 부분은 가볍게 읽어만 보고, 자세한 리뷰는 생략하겠습니다.

영어->독어 번역, 영어->불어 번역 문제에서 다른 기존의 SOTA 모델들과 비교하여 월등한 최고 성능을 보여주었다는 것이 핵심입니다.



결론 Conclusion

결론에서는 앞으로 트랜스포머가 언어 번역뿐만 아니라 이미지, 오디오, 비디오와 같은 대용량 입출력 처리를 할 수 있도록 어텐션 메커니즘을 더 연구하고 발전시켜 나가겠다고 밝히며, 트랜스포머에 대한 자부심과 앞으로에 대한 기대감으로 마무리를 하고 있습니다.

2017년에 발표되었던 논문인 만큼 2024년인 현재까지 트랜스포머는 많은 발전에 발전을 거듭하였고, 최신 생성형 모델의 근간이 되고 있습니다. 어느 분야든 마찬가지겠지만 특히 딥러닝 분야에서 기존의 문제점을 해결하기 위한 새로운 아이디어를 떠올리고 그를 구현해내는 이런 멋진 작업물들을 볼 때면 정말 경이롭습니다. 이번 논문 리뷰를 통해서 트랜스포머의 기본 개념과 원리, 구조에 대해 파악할 수 있어서 영광이었습니다.

이로서 Attention is all you need, 트랜스포머 논문 리뷰를 마치겠습니다. 감사합니다 :)

포스팅 참고 문헌

1. Do it! 딥러닝 교과서 (윤성진 지음)
2. MIT 6.S191: Recurrent Neural Networks, Transformers, and Attention (바로가기)
3. https://colah.github.io/posts/2015-08-Understanding-LSTMs/
4. SK플래닛 T아카데미 강사님 수업자료

RNN

[1] 시퀀스 데이터

흔히 '시계열 데이터', '순차 데이터'라고도 부르는 Sequence data에는 시공간적 순서 관계가 포함되어 있습니다. 예를 들어서 지금 저는 커피 한 모금을 마시고 컵을 내려놓은 뒤 타자를 치고 있고, 창 밖에는 차와 사람들이 움직이고 있으며, 옆에 있는 친구는 음악을 듣고 있어요. 이러한 일들이 일어날 때 자연스레 시간이 흐르고 공간상의 움직임도 나타나게 되지요. 이런 시퀀스 데이터는 문맥(context)을 가지기 때문에 어느 한 순간의 데이터만 살펴봐서는 그 특성을 이해하기가 어렵습니다.

예를 들어 우리가 위의 그림만 보고 공이 다음에 어느 방향으로 움직일지 논리적으로 예측하기는 무척 어렵습니다. 공이 옆에서 굴러왔을지, 밑에서 떨어졌을지, 위에서 던졌을지, 시공간 정보에 대한 어떤 문맥도 주어지지 않았기 때문입니다.

반면에 이렇게 공이 움직여 온 과정 대한 정보가 주어진다면, 우리는 아주 자연스럽게 공이 오른쪽 방향으로 움직일 것이라고 합리적으로 예측할 수 있게 됩니다. 이렇게 데이터의 순차 구조를 인식하고 처리할 수 있는 인공 신경망이 바로 RNN : 순방향 신경망(Recurrent Neural Network)입니다. RNN은 아래와 같은 구조로 이루어져 있습니다.


[2] RNN 기본 구조와 원리

RNN의 구조

첫 번째 입력 x1에 weight X를 곱한 값 => 히든 레이어 h1
{(두 번째 입력 x2에 weight X를 곱한 값) + (전 단계 h1에 weight H를 곱하고 bias를 더한 값)}에 하이퍼탄젠트(tanh) 액티베이션 펑션을 걸어주면 => 히든 레이어 h2
{(세 번째 입력 x3에 weight X를 곱한 값) + (전 단계 h2에 weight H를 곱하고 bias를 더한 값)}에 하이퍼탄젠트(tanh) 액티베이션 펑션을 걸어주면 => 히든 레이어 h3
....
{(t 번째 입력 xt에 weight X를 곱한 값) + (전 단계 h(t-1)에 weight H를 곱하고 bias를 더한 값)} 하이퍼탄젠트(tanh) 액티베이션 펑션을 걸어주면 => 히든 레이어 ht

이를 수식으로 나타내면 아래와 같아집니다.


[3] RNN의 weight

RNN에서 사용되는 가중치 Wx, Wh, Wy는 모든 시간 단계에서 공유되는데, 그 이유는 다음과 같습니다.

  1. 순차 구조를 포착할 수 있기 때문입니다.
    :  RNN의 주된 목적 중 하나는 시퀀스 데이터의 순차적 특성을 학습하는 것입니다. 이를 위해서는 각 시간 단계에서 입력과 은닉 상태를 동일한 방식으로 처리해야 합니다. Wx(입력 가중치), Wh(은닉 상태 가중치), Wy(출력 가중치)를 시간 단계마다 공유함으로써 네트워크는 시간 순서에 따라 일관된 방식으로 데이터를 처리할 수 있습니다.
  2. 가변 길이 데이터 처리가 용이하기 때문입니다.
    :  RNN은 고정된 길이의 입력만 처리하는 것이 아니라 가변 길이의 시퀀스 데이터도 처리할 수 있습니다. 모든 시간 단계에서 동일한 가중치를 사용하면, 시퀀스의 길이가 어떻게 되든 간에 동일한 모델 구조로 일관된 학습과 예측이 가능해집니다. 따라서 입력 시퀀스의 길이가 다르더라도 동일한 가중치 매트릭스를 사용하여 각 단계의 데이터를 처리할 수 있습니다.
  3. 파라미터 수가 절약되고, 정규화 효과가 생깁니다.
    :  각 시간 단계마다 다른 가중치를 사용한다면 파라미터 수가 급격히 증가하여 학습이 비효율적이 될 수 있습니다. 모든 시간 단계에서 가중치를 공유함으로써 파라미터 수를 크게 줄일 수 있습니다. 이는 모델의 복잡도를 낮추고, 과적합(overfitting)을 방지하는 데 도움이 됩니다. 또한, 적은 수의 파라미터를 통해 더 나은 일반화 성능을 얻을 수 있습니다. 이는 정규화 효과와 유사한 역할을 하여 모델이 더 일반화된 패턴을 학습하는 데 기여합니다.

[4] RNN Loss Function & Backpropagation

RNN의 전체 Loss Function은 모든 단계의 Loss Function을 더해서 정의합니다. 각 단계의 Loss Function은 회귀 문제라면 주로 MSE, 분류 문제라면 주로 Cross Entropy로 정의하게 됩니다.

RNN의 Backpropagation은 BPTT(Back Propagation Through Time), 시간펼침 역전파 알고리즘이라고도 불리는데요. 말그대로 시간 순서대로 네트워크를 펼쳐서 역전파를 수행하는 알고리즘입니다. BPTT는 아래와 같은 단계로 이루어집니다.

BPTT

1. 모든 시간 단계(t)마다 오차를 계산하고, 이를 이전 시간 단계로 전파합니다.
2. 모든 단계에서 가중치 및 바이어스에 대한 기울기를 구합니다.
3. 구한 기울기를 이용하여 각 가중치와 바이어스를 업데이트합니다.
-> 이를 통해 모델이 점차 학습하여 더 나은 예측을 할 수 있게 됩니다.

 


[5] RNN 한계점

예를 들어서, 한국에서 태어난 수리링은 인공지능 공부를 열심히 하고 있는데, 수리링은 영어도 잘 하지만 사실 한국말을 제일 잘한다. 라는 문장이 있다고 해 봅시다. RNN 모델에게 빈칸에 들어갈 말이 무엇인지 추측하도록 시킨다고 할 때, 모델이 정답을 잘 맞추기 위해서는 문장의 가장 처음에 있는 '한국'이라는 단어의 정보를 제대로 활용할 수 있어야 합니다. 만약 '한국'이라는 단어의 중요도가 점점 소실된다면 뜬금없이 빈칸에 '스페인어'나 '영어' 등의 오답이 들어가는 경우가 발생하겠죠. 이것이 바로 RNN의 근본적인 문제점입니다. (아, 참고로 빈 칸에 들어갈 정답은 '한국말' 입니다. ^^)

Long-term Dependency

RNN은 오차가 멀리 전파될수록(시간이 지날수록) 기울기가 점차 작아지는(0으로 수렴하는) 'Vanishing Gradient'로 인해 입력 데이터의 영향이 점점 사라지는 'Long-term Dependency'라는 명확한 한계를 가지고 있습니다. 또 가중치가 반복적으로 곱해지는 과정에서 기울기가 폭발적으로 발산하는 불상사로 인해 정상적인 학습이 불가능해지는 Exploding Gradient 문제도 쉽게 발생하곤 합니다. 이러한 문제점을 해결하기 위해 gradient clipping이나 오차를 몇 단계까지만 전파시키는 생략된-BPTT(truncated BPTT) 등을 사용하기도 했지만, 시간이 지나면서 사람들은 LSTM 및 GRU를 많이 사용하게 되었습니다.



LSTM

[1] 핵심

LSTM은 기본 RNN 구조를 변경해서 만든 모델입니다. LSTM의 핵심 아이디어는 바로 Gate & Cell State인데요.

이전의 RNN은 모든 정보를 연쇄적으로 곱해주는 방식으로 계산했기 때문에 역전파 과정에서 Vanishing/Exploding Gradient 같은 문제점이 발생했습니다. 이를 방지하고자 LSTM은 게이트가 있는 Gated cell을 사용하여 선택적으로 정보를 조절해 학습할 수 있도록 합니다. 이는 장기 기억(Long Term Memory)과 단기 기억(Short Term Memory)이라는 두 가지 메커니즘(LSTM)으로 구현되는 것으로 볼 수 있어 LSTM이라는 이름이 붙게 되었습니다.

기본 RNN은 위처럼 매우 심플한 구조로 이루어져 있습니다. (Xt * Wx + Ht-1 * Wh)에 활성함수로 하이퍼탄젠트(tanh)를 곧바로 걸어주게 되고, 이것이 순차 반복적으로 곱해지면서 Vanishing/Exloding Gradient 문제가 발생하게 되는 건데요.

LSTM의 경우, 내부 구조가 위의 사진처럼 세부 단계로 나뉘어져 있습니다. 처음 보면 무척 복잡해 보이지만, 한 번 이해하고 나면 꽤나 심플한 아이디어로 구성되어 있음을 알 수 있게 됩니다.


[2] Gate

Cell State : C

LSTM을 이끄는 핵심은 Cell state, 그림 상단에 보이는 Ct 라인입니다. Cell state는 마치 컨베이어 벨트와 역할을 하면서 레이어 사이를 지나가는데요. LSTM은 이 Cell state에 새로운 정보를 선택적으로 추가하기도 하고, 기존의 정보를 삭제하기도 합니다. 근데 도대체 어떻게 이런 일이 가능한 걸까요? 만약 이런 의문이 드신다면, 당신은 정상인입니다. 바로 게이트(Gate)가 있기 때문에 가능합니다.

그럼 정확히 Gate가 무엇이냐?라고 하신다면, 참고문헌 [3]은 They are composed out of a sigmoid neural net layer and a pointwise multiplication operation이라고 기술하고 있습니다.

gate
sigmoid neural net layer
: 시그모이드 함수는 0부터 1 사이의 값을 출력하기 때문에, 비율의 관점에서 바라볼 수 있습니다. 시그모이드 출력값이 0이라면 정보를 삭제하는 것과 같을 것이고, 시그모이드 출력값이 1이라면 정보를 100% 통과시켜야 함을 의미하겠죠.

pointwise multiplication operation
: 
시그모이드 함수의 출력값, 즉 '비율'을 곱한 다음, 이렇게 중요도가 결정된 정보가 게이트를 통과하게 되는 것입니다.

즉, 그냥 단순히 정보를 마구잡이로 곱하는 방식이 아니라, 각 정보의 중요도를 반영한 값을 처리할 수 있는 매커니즘을 구현하겠다는 겁니다. 만약 쓸데 없는 정보가 들어온다면 입뺀을 먹이고 통과를 시켜주지 않을 것이고, 필요한 정보가 들어온다면 중요도에 따라 필터링을해서 통과시켜 주겠다는 것이죠. LSTM은 세 가지 주요 게이트를 사용하여 정보를 조절합니다.


[3] Gate 종류

  • Forget Gate : 과거 정보를 얼마나 유지할 것인지
  • Input Gate : 현 시점의 입력 정보를 Cell State에 얼만큼 반영할 것인지
  • Output Gate : 현 시점에서 정보를 얼만큼 출력해서 다음 시점에 제공할지

3-1. Forget Gate 

현 시점의 입력 정보 Xt와 지난 레이어h(t-1)를 종합한 정보에 시그모이드를 걸어 비율값을 만들어 준 다음, 이전의 Cell State와 곱해줍니다. 시그모이드 값이 작을수록 이전의 정보는 많이 소실되고, 시그모이드 값이 클수록 이전의 정보는 많이 유지되겠죠. 즉, forget gate는 과거 정보를 얼마나 유지할 것인지 판단하는 게이트입니다. 

3-2. Input Gate 

인풋 게이트는 다음과 같은 구조로 흘러갑니다.

현 시점의 입력 정보 Xt와 지난 레이어h(t-1)를 종합한 정보에 

  1. 하나는 시그모이드를 걸어 0과 1사이의 비율값으로 만들어줍니다.
  2. 또 하나는 RNN처럼 활성함수로 하이퍼탄젠트(tanh)를 걸어줍니다.
  3. 두 개의 값을 곱해줍니다.
  4. Forget Gate를 지난 Cell State에 그 값을 더해줍니다.

즉, 인풋 게이트는 현 시점의 입력 정보를 Cell State에 얼만큼 반영할 것인지 판단하는 게이트입니다. 인풋 게이트를 통과한 정보가 더해지고 나면, 현 시점 LSTM에서의 Cell State가 완성됩니다. 이 Cell State는 다음 시점의 LSTM으로 전달되어, 또 다시 Forget Gate를 통과한 정보와 곱해지게 되겠죠. 

3-3. Output Gate 

아웃풋 게이트는 다음과 같은 구조로 흘러갑니다.

  1. 현 시점의 입력 정보 Xt와 지난 레이어h(t-1)를 종합한 정보에 시그모이드를 걸어 0-1사이의 비율값으로 만들어 줍니다.
  2. Cell State 값에 활성함수로 하이퍼탄젠트(tanh)를 걸어줍니다.
  3. 두 값을 곱해서 다음 시점의 h값으로 제공합니다.

즉, 현 시점의 output payer를 통과한 값은 다음 시점의 입력 정보와 만나는 h값이 되는 것입니다. 따라서 Output layer는 현 시점에서 정보를 얼만큼 출력해서 다음 시점에 제공할지 판단하는 레이어라고 볼 수 있습니다. 참고문헌 [3]에서는 Output gate를 지난 output을 다음과 같이 표현하고 있습니다. This output will be based on our cell state, but will be a filtered version. 


[4] LSTM의 장점

LSTM에서 셀들의 상태를 연결하는 경로를 다시 한 번 살펴보겠습니다.

LSTM은 셀들의 상태를 연결하는 경로에서 행렬곱을 생략하고, 대신 Cell State 개념을 도입했습니다. 기억 정보들은 Gate를 지나면서 선택적으로 형성, 지속, 망각되는 과정을 거치고, 그 과정이 담긴 Cell State에서 단기 기억과 장기 기억이 상호작용하며 유지됩니다. 이러한 획기적인 아이디어가 기존 RNN에서 Vanishing Gradient를 유발하는 요인이었던 반복적인 행렬 곱 연산을 대체하게 되면서 중요한 정보가 시퀀스의 초반부에서 후반부까지 소멸되지 않고 유지될 수 있었고, Long-term Dependency 문제를 해결할 수 있었습니다.



GRU

GRU(Gated Recurrent Unit)는 LSTM(Long Short-Term Memory)의 단순화된 버전으로, 구조를 단순화해서 계산 효율성을 높인 모델입니다.

출처 : http://dprogrammer.org/rnn-lstm-gru

  • Cell state와 Hidden state의 통합
    • 가장 도드라지는 차이점은 Cell State가 사라졌다는 점입니다.
    • LSTM은 Cell state(상단 C)와 Hidden state(하단 h)를 별도로 유지합니다. Cell state는 장기 기억을 저장하고, Hidden state는 단기 기억과 출력을 담당합니다.
    • 반면에 GRU는 둘을 통합하여 단일 Hidden state로 처리합니다. 이를 통해 모델이 더 간단해지고, 계산 효율성이 향상됩니다.
  • 게이트의 변화
    • LSTM은 세 가지 게이트를 가지고 있습니다. (Forget/Input/Output Gate)
    • GRU는 두 가지 게이트를 가지고 있습니다. (Reset/Update Gate)
    • 리셋 게이트(r)는 이전 은닉 상태를 얼마나 초기화할지를 결정하고, 업데이트 게이트(z)는 새로운 정보와 기존 정보를 어떻게 결합할지를 결정합니다.

GRU는 이러한 구조적 단순화를 통해 LSTM의 복잡성을 줄이면서도 유사한 성능을 유지할 수 있도록 설계되었습니다. 만약 컴퓨팅 리소스가 제한된 환경에 있다면, LSTM보다 계산량이 적으면서도 비슷한 성능을 가진 GRU를 사용하는 것이 효율적이겠습니다.


이것으로 RNN / LSTM / GRU 포스팅을 마치도록 하겠습니다. 다음 포스팅에서는 Transformer 논문 리뷰를 해보겠습니다 :) 감사합니다!

 
6-9주차는 머신러닝 팀프로젝트와 발표가 있었던 주간이었습니다. 저희 조는 <유투브 뷰티 마케팅 영상의 반응율 예측>이라는 주제로 프로젝트를 진행했고, 발표 이후 디벨롭 과정을 거쳐 <유튜브 마케팅 영상의 조회수 예측 - 뷰티 쇼츠 영상을 중심으로>라는 새로운 주제로 다가오는 7월 학회 발표를 준비하고 있습니다.
지난 개인 EDA 프로젝트 발표 이후 작성한 회고와 마찬가지로 이번 팀 프로젝트 회고 역시 잘한 점과 개선할 점을 중점적으로 작성해 볼텐데요. 개인 프로젝트와 조금 다르게 이번 포스팅에서는 팀 프로젝트를 진행하면서 어떤 문제상황이 있었는지, 그리고 부족했던 점을 어떻게 개선했는지를 중점적으로 적어보도록 하겠습니다.


 

1. 문제의식

저희 조는 유투브 뷰티 광고 영상의 메타데이터를 수집해서 조회수, 좋아요 수, 댓글 수로 구성된 '반응률'이라는 마케팅 지표를 예측하는 프로젝트를 진행했는데요. 뷰티 업계와 광고주에게 어필할 수 있는 실용적인 프로젝트라는 믿음은 있었지만, 프로젝트에 기승전결이 있는지, 주제와 결론이 부합하는지에 대해서는 프로젝트를 진행하는 내내 의문이 들었어요. 그러다 보니 발표 자료를 구성하면서 급히 짜맞추고 끼워맞추는 부분이 다수 발생했고, 프로젝트의 완성도가 성에 차지 않았습니다. 그 이유를 돌아보니 다음과 같습니다.
첫째, 프로젝트를 진행하는 내내 세부 주제를 계속해서 조금씩 틀고 변경하게 되면서 전체적인 흐름과 구성에 일관성이 없었습니다. 저희 팀은 막연히 '유투브 광고에 관련된 무언갈 하자'라는 생각으로 데이터를 수집하고 들여다 보면서 발표 직전에야 '뷰티 광고 영상'을 타겟으로 하자는 결론을 내릴 수 있었는데요. 로우 데이터를 분석하며 프로젝트 아이디어를 얻는 것이 때로는 좋은 방법이 될 수는 있겠지만, 제한된 시간 안에 기승전결을 도출해야 했던 이번 과제에 있어서는 다소 효율적이지 못한 접근 방법이었다고 생각합니다.
둘째, 어떠한 프로젝트를 진행할 때에는 기존에 어떤 선행 연구가 이루어졌는지에 대한 데이터 분석이 필수적으로 이루어져야 하는데, 저희 조는 시간에 쫓기면서 이 점을 간과했습니다. 그러다 보니 유투브의 뷰티 마케팅 영상에 대한 연구사례가 있는지 없는지조차 알지 못했고, 선행 연구에 비교했을 때 우리 프로젝트에는 어떤 차별점이 있는지조차 파악할 수 없었습니다. 내 프로젝트가 가지는 강점과 의의에 대해 스스로 설득이 되지 않으니 프로젝트 막바지로 갈수록 '이게 맞나?'라는 생각이 들 수밖에 없었습니다.
셋째, 소통의 부족으로 유기적인 팀플레이가 다소 부족했고 역할 분담이 효율적으로 이루어지지 못했습니다. 저희 팀원 모두 열의가 가득했고 개별 능력치도 월등히 좋았던지라 이 점이 더욱 아쉬웠어요. 모두 적극적으로 프로젝트에 참여했고, 열린 마음으로 소통을 하려고 했지만, 저를 포함한 팀원 모두가 데이터 기반의 소통을 해본 경험이 부족했던 것이 원인이라고 생각하고 있습니다. 


2. 개선방향

프로젝트를 진행하면서 겪은 시행착오를 통해 얻은 값진 교훈을 토대로, 저는 저의 팀 프로젝트 결과물을 다음과 같이 디벨롭하고 있습니다.

  1. 로우 데이터를 분석하면서 뷰티 카테고리의 영상 데이터 분포도가 운동, 패션, 테크, 반려동물, 등의 다른 카테고리와 다양한 차별점이 있는 점을 발견했는데요, 특히 타 카테고리와 달리 뷰티 카테고리의 광고 영상은 평균 2.5배 이상 쇼츠 영상의 비율이 많은 것을 확인할 수 있었습니다. 이를 통해 유튜브의 뷰티 광고 영상 중에서도 쇼츠 영상을 타겟팅하여 연구를 진행하면 의의가 있을 것이라고 생각했습니다.
  2. 유투브 영상의 조회수 예측에 관한 선행 연구, 숏폼 광고 영상에 관한 선행 연구, 유튜브 뷰티 카테고리 영상에 관한 선행 연구를 최대한 많이 조사했습니다. 그 결과, 유투브 영상의 조회수 예측에 관한 선행 연구는 활발히 이루어진 바 있으나 뷰티 광고 영상만을 타겟팅한 연구 사례는 존재하지 않으므로 우리 연구에 확실한 차별점이 있을 뿐더러 뷰티 광고 업계에 필요한 인사이트를 제공하는 선구자 역할을 할 수 있다는 근거를 확보했습니다.
  3. 기존 프로젝트에서는 마케팅 수치 중 하나인 반응률이라는 지표를 예측하는 것을 목표로 했습니다. 하지만 광고 업계에서 반응률이라는 지표가 하나의 통일된 공식으로 존재하지 않으며 업체마다 필요에 따라 수정해서 쓰는 관례가 있음을 확인했습니다. 게다가 '반응률'이라는 수치가 무엇을 의미하는지 비교할 수 있는 대상이나 선행 연구사례가 없어 해석이 난해한 점을 고려해 조회수 예측이라는 전통적인 모델을 구성하는 것으로 방향을 수정하기로 결정했습니다. 예측한 조회수는 누구나 직관적으로 받아들이고 해석하기 쉬운 장점이 있습니다.
  4. 기존 프로젝트에서는 영상의 조회수, 구독자수, 길이, 태그 빈도 등 메타데이터만을 수집하고 분석하여 모델을 구축했습니다. 그러다보니 영상 내부의 시청각 데이터에 관한 고려를 전혀 하지 못했을 뿐더러, 인플루언서로서 영향력을 가지는 유투버에 대한 피처 추출도 간과하게 되었습니다. 이번 프로젝트에서는 뷰티 업계에서 전문성을 가진 인플루언서로 판단될 만한 국내 뷰티 유투버 192명을 직접 선정하고, 이 유투버들의 광고 영상의 메타데이터와 함께 시청각 데이터를 수집하고 분석하여 모델을 구축했습니다.
  5. 기존 프로젝트는 발표자료를 잘 구성하여 취업을 위한 포트폴리오로 제작하는것을 목표로 삼았습니다. 그러나 1-4와 같은 일련의 데이터 기반의 의사결정을 통해 우리의 연구가 학술지로서 충분한 가치를 가진다는 합리적인 판단을 내리게 되었고, 새로 디벨롭하는 프로젝트는 학회에 발표하는 저널 형식으로 완성하기로 결정했습니다. 저희 팀은 6월 중순까지 프로젝트를 마무리하여 7월 학회에 발표하는 것을 목표로 하고 있습니다.

3. 마무리

이번 프로젝트에서 '데이터 기반의 의사결정'을 통한 프로젝트 기획과 구성이 중요하다는 정말 값진 교훈을 얻었습니다. 우리는 협업을 하면서 끊임 없이 서로를 설득시키는 과정을 반복합니다. 때로는 그 설득이 내가 옳다일 수도 있고, 네가 틀렸다일 수도 있겠죠. 나를 이해하지 못하는 팀원을 설득하는 일, 이해가 되지 않는 팀원의 말을 계속해서 듣는 일은 생각보다도 더 어렵고 소모적입니다. 때로는 일을 시작하기도 전에 말을 하다가 진이 다 빠지기도 하고요.이럴 때 필요한 것이 바로 데이터입니다. 번지르르한 말 천 마디보다 하나의 데이터가 더 큰 설득력을 가질 수 있습니다.우리는 데이터를 기반으로 의사소통을 하고, 의사결정을 내릴 수 있어야 한다는 걸, 이번 프로젝트를 통해 깨달았어요. 이렇게 얻은 깨달음을 토대로 더 나은 방향으로 팀 프로젝트를 디벨롭할 수 있어서 정말 감사하게 생각하고 있습니다. 제가 구상한 개선 방향을 믿고 따라와주는 멋진 팀원들에게 감사하다고 말하고 싶어요.
추가로 말씀드리자면, 지난 회고부터 틈틈이 언급했던 대로, 모든 KDT 교육과정이 대부분 비슷하겠지만, ASAC 빅데이터 분석과정은 특히 교육과정이 매우 타이트합니다. 그럼에도 불구하고 수업만 듣는다면 남는 게 없어요. 내가 무엇을 배우고 공부하는지 뭐라도 흔적을 남기고 아웃풋을 만들기 위해서는 수업 이상으로 틈틈이 꾸준히 노력해야 합니다. 더불어 본인에게 부족한 부분은 스스로 파악하고 자기주도적으로 보충해야 하고, 그와 동시에 포트폴리오도 구축해야 하며, 본인이 희망하는 필드에 걸맞는 자격을 갖추기 위한 추가 공부도 진행해야 합니다. 저는 그렇게 살고 있어요. 하루 24시간이 모자랄 만큼 바쁘지만, 그래도 누가 떠먹여주는 것보다 내가 스스로 개척해나가는 미래가 더욱 값지고 빛날 것이라는 일념으로 즐겁게 생활하고 있습니다.


다음 회고에는 딥러닝 프로젝트 회고를 가지고 오겠습니다 :) 감사합니다.
 

VGGNet

VGGNet(Visual Geometry Group Network)은 2014년 1000개의 이미지 클래스를 분류하는 이미지넷 이미지 인식 대회에서 준우승을 한 모델입니다. 옥스포드 대학의 연구팀 VGG에 의해 개발되었다고 하는데요. 이번 포스팅에서는 VGGNet 논문에서 중요한 내용을 살펴보고 직접 코드화하는 과정을 정리해 작성해 보겠습니다.


<VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION>
논문 링크 : https://arxiv.org/pdf/1409.1556

VGG의 핵심 장점은 다음과 같습니다.

1. convolution의 depth를 깊게 구성
2. 연산량을 획기적으로 줄여 좋은 성능 확보
3. 깊은 레이어 구조를 통해 Activation function을 여러 번 집어넣어서 비선형성을 더 많이 확보

왜 이런 장점이 있는지 논문 내용과 함께 살펴봅시다.


2.1. Architecture

VGG 이전의 기존 CNN 모델들은 주로 Convolution 레이어와 Pooling 레이어를 번갈아 연속적으로 사용하는 것이 일반적이었습니다. VGGNet은 이러한 기존의 틀에서 벗어나 Convolution을 2번 또는 3번 연속해서 쌓은 뒤 Pooling 레이어를 배치하는 새로운 구조를 제시합니다. 

VGG16 구조

위 사진은 대표적인 VGG16모델의 레이어 설계도입니다. 책이나 인터넷에서 쉽게 찾아볼 수 있는 그림인데요. 보이는 것처럼 파란색의 Convolution 레이어가 연속해서 2-3번 쌓이고, 이어서 빨간색 max pooling layer가 배치된 것을 볼 수 있습니다. 논문 2.1 Architecture에 관한 부분을 함께 살펴보도록 하겠습니다.

the input to our ConvNets is a fixed-size 224 × 224 RGB image
  • 입력 이미지의 shape은 (224, 224, 3)인 것을 알 수 있습니다.
The image is passed through a stack of convolutional (conv.) layers, where we use filters with a very small receptive field: 3 × 3 (which is the smallest size to capture the notion of left/right, up/down, center). (중략) The convolution stride is fixed to 1 pixel
  • 콘볼루션 레이어를 여러겹 쌓고 필터는 (3 x 3) 작은 사이즈로 고정하여 사용한다고 명시하고 있습니다. VGG 이전의 CNN 모델들은 전통적으로 필터 사이즈가 7, 9, 11 정도로 큰 것이 일반적이었다고 합니다. 그래서 사이즈가 3인 필터는 'very small'이라고 표현되는 것으로 보입니다.
  • 콘볼루션 레이어의 stride는 1픽셀로 고정됩니다.
Max-pooling is performed over a 2 × 2 pixel window, with stride 2.
  • 사용된 Maxpooling의 필터 사이즈와 stride 모두 (2, 2)인 것을 알 수 있습니다.
A stack of convolutional layers is followed by three Fully-Connected (FC) layers: the first two have 4096 channels each, the third performs 1000- way ILSVRC classification and thus contains 1000 channels (one for each class). The final layer is the soft-max layer.
  • CNN 레이어 이후에는 순서대로 4096 - 4096 - 1000개의 필터가 사용된 FC(Fully-Connected) 레이어가 3번 배치되었습니다.
  • 마지막 필터가 1000개인 이유는 VGG모델이 총 1000개의 이미지 클래스를 분류하기 때문입니다.
  • 마지막 레이어의 activation function로는 소프트맥스가 사용된 것을 알 수 있습니다.

2.2. Configuration

다음으로 여러 가지 버전의 VGG모델을 표현된 논문의 Table 1, 2를 함께 살펴보겠습니다.

The width of conv. layers (the number of channels) is rather small, starting from 64 in the first layer and then increasing by a factor of 2 after each max-pooling layer, until it reaches 512. 
  • 콘볼루션 레이어의 필터의 수는 64개로 작게 시작해서 마지막으로 512가 될때까지 2의 배수로 늘려 가겠다고 말하고 있습니다.
The convolutional layer parameters are denoted as “conv[receptive field size]-[number of channels]”. The ReLU activation function is not shown for brevity.
※ [2-1] All hidden layers are equipped with the rectification (ReLU (Krizhevsky et al., 2012)) non-linearity.
  • Table 1에서 표현된 레이어 수식(conv0-000형식)을 읽을 수 있어야 합니다. 예를 들어 conv3-256의 경우 필터 사이즈 (3x3)인 콘볼루션 레이어가 256장 쌓인 것으로 해석할 수 있습니다.
  • 간결한 표현을 위해 아래 배치도(Table 1)에서 활성함수 ReLU는 생략해서 표현되었으나, 논문 2.1 Architecture에 언급되었듯이 모든 히든레이어에는 ReLU가 적용되었습니다.

Table 1

  • 우리가 맨 위에서 살펴본 빨강파랑 레이어 구조도는 VGG16모델입니다. Convolution 레이어와 Dense 레이어를 합해 16개의 레이어가 사용되었습니다. 여기에서 레이어가 3개 늘어나면 VGG19(E모델)이 됩니다.
  • VGG16에 해당하는 모델 C와 D의 차이점은 3겹의 콘볼루션 레이어에서 마지막에 필터 사이즈 1짜리 레이어가 쓰였느냐 3짜리가 쓰였느냐 정도로 구분이 되네요.

VGG16 구조
Table 2

In Table 2 we report the number of parameters for each configuration. In spite of a large depth, the number of weights in our nets is not greater than the number of weights in a more shallow net with larger conv. layer widths and receptive fields (144M weights in (Sermanet et al., 2014)).
  • 테이블 2에는 백만 단위의 파라미터 수가 표현이 되어 있는데요. VGG16에 해당하는 모델 C-D의 경우 1억 3천만 개 정도의 파라미터를 찾아야 하네요.
  • 그렇다면 사이즈 3의 작은 필터를 사용한 3개의 콘볼루션 레이어를 연속적으로 사용해서 얻은 이점이 무엇일까요?

Table 2 아래에 중요한 내용이 담겨있습니다.

So what have we gained by using, for instance, a stack of three 3×3 conv. layers instead of a single 7×7 layer? First, we incorporate three non-linear rectification layers instead of a single one, which makes the decision function more discriminative. Second, we decrease the number of parameters
예: (10, 10) 이미지에 (7, 7) 필터 1번 적용
* 콘볼루션 후 최종 사이즈 : (4, 4)
* 찾아야 할 파라미터  : (7, 7) -> 49개
예 : (10, 10) 이미지에 (3, 3) 필터 3번 적용
* 콘볼루션 후 최종 사이즈 : (4, 4)
* 찾아야 할 파라미터 : (3, 3) 3개 -> 9 * 3 -> 27개
  • 최종적으로 얻는 이미지의 사이즈는 (4, 4)로 동일한데 찾아야 할 파라미터의 수는 49개에서 27개로 줄어드는 것을 확인할 수 있습니다.
  • 콘볼루션 레이어를 연속해서 쌓는 VGG 구조를 통해 찾아야 할 파라미터 수를 획기적으로 줄여 연산량을 줄이고 성능을 개선할 수 있다는 것입니다.
  • 또한 한 번의 콘볼루션 레이어만 추가했을 때보다 세 번의 연속적인 콘볼루션 레이어를 추가하게 되면서 비선형성을 더욱 강조해 모델의 유연성을 확보할 수 있게 되었고 이 역시 성능 개선으로 이어지게 되었습니다.

코드화

VGG16 모델 D - 코드로 구현해보기

from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten, Dense, Dropout

필요한 라이브러리를 호출합니다.

# 논문 VGG16 D모델 그대로 설계해보기
    
model_D_VGG_origin = tf.keras.Sequential([
    Conv2D(64, 3, input_shape = (224, 224, 3), padding = "same", activation = "relu"),
    Conv2D(64, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Conv2D(128, 3, padding = "same", activation = "relu"),
    Conv2D(128, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Conv2D(512, 3, padding = "same", activation = "relu"),
    Conv2D(512, 3, padding = "same", activation = "relu"),
    Conv2D(512, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Flatten(),
    Dense(4096, activation = "relu"),
    Dense(4096, activation = "relu"),
    Dense(1000, activation = "softmax") # 1000가지 이미지를 분류하는 대회였음
])


VGG16 모델 D - Fashion Mnist에 맞게 튜닝하기

VGG16 Model D를 튜닝해서 fashion_mnist 분류모델을 구현하는 코드로 작성해 보겠습니다.

  • 논문 상 입력 데이터는 (224, 224, 3)이지만 Fashion Mnist의 데이터는 (28, 28, 1)입니다. 28을 224로 늘리는 것은 해상도가 너무 깨지고 특징이 소실되므로 의미가 없다는 판단 하에, 입력 부분을 28로 튜닝해서 사용하기로 결정했습니다.
  • MaxPool 횟수를 4회에서 3회로 줄이기로 했습니다. 이미지 사이즈가 가로 세로 (28, 28)이기 때문에 논문 그대로 4번 사용하면 사이즈가 소실될 수도 있습니다.
  • 논문 속 레이블은 1000개이지만 Fashion Mnist의 레이블은 10개입니다. 따라서 마지막 Dense 레이어의 필터 수를 1000에서 10으로 줄이겠습니다.
import tensorflow as tf

# 데이터 불러오기
fashion_mnist = tf.keras.datasets.fashion_mnist
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

# 정규화
X_train, X_test = X_train/255. , X_test/255.

# 3D를 4D로 변환
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
# 논문 VGG16 D모델 -> 패션 엠니스트에 맞는 모델로 바꾸어보기
    
model_D_VGG_fashion = tf.keras.Sequential([
    Conv2D(64, 3, input_shape = (28, 28, 1), padding = "same", activation = "relu"),
    Conv2D(64, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Conv2D(128, 3, padding = "same", activation = "relu"),
    Conv2D(128, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    Conv2D(256, 3, padding = "same", activation = "relu"),
    MaxPool2D(pool_size = 2, strides = 2),
    Flatten(),
    Dense(4096, activation = "relu"),
    Dense(4096, activation = "relu"),
    Dense(10, activation = "softmax") # 10가지 이미지로 분류하기
])
model_D_VGG_fashion.summary()

model_D_VGG_fashion.compile(loss = tf.keras.losses.SparseCategoricalCrossentropy(),
                 optimizer = Adam(),
                 metrics = ['accuracy'])
import os

# callbacks
early = tf.keras.callbacks.EarlyStopping(patience = 5)
cp_path = "training/cp-{epoch:04d}.ckpt"
cp_dir = os.path.dirname(cp_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(cp_path,
                                                 monitor = 'val_loss',
                                                 verbose = 1,
                                                 save_weights_only=True)

# train
history_D_fashion = model_D_VGG_fashion.fit(X_train,
                                            y_train,
                                            validation_split = 0.25,
                                            batch_size = 128,
                                            epochs = 200,
                                            callbacks = [early,cp_callback])

model_D_VGG_fashion.evaluate(X_test, y_test)

# 313/313 [==============================] - 2s 5ms/step - loss: 0.2621 - accuracy: 0.9195
# [0.2621194124221802, 0.9194999933242798]

VGG16모델을 튜닝해서 사용한 결과 단 한 번의 시도만에 0.92에 가까운 valid accuracy를 확보하게 되었습니다 :)


 
 
이것으로 간단하게 살펴본 VGG 논문 리뷰를 마치겠습니다. 감사합니다 :)

주의 : 본 회고의 모든 내용은 글쓴이(수리링) 본인이 처음부터 끝까지 직접 작성했습니다. 본 회고를 참고하시거나 활용하실 분들께서는 반드시 사전에 댓글 또는 이메일로 목적을 설명하시고 글쓴이의 동의를 구하시기 바랍니다.
(이메일 : niceonesuri@gmail.com)

[목차] 
1. 기조 연설 회고
2. AWS 생성형 AI 체험 & 기업 부스 회고(kt ds)
3. 데이터분석 강의 회고
4. 깨알 보너스 (공짜 점심, 자격증존, etc)

2024년 5월 17일 (금) 서울 코엑스에서 열린 AWS Summit Seoul에 참여했습니다. 올해 써밋 서울은 16-17일 2일간 열렸는데요, 저는 현재 참여중인 SK플래닛 T아카데미 수업과 팀프로젝트 일정으로 인해 양일 모두 참석하긴 어려워 금요일 하루만 참석을 했습니다. AWS CCP 자격증을 취득한 이후로 아마존의 클라우드 서비스와 클라우드 엔지니어링에 아주 큰 흥미와 호기심을 가지고 있었던 저에게는 꿈만 같은 하루였는데요. 제가 참여한 2일차 AWS Summit Seoul에서 느낀 3가지 키워드를 말씀드리면 다음과 같습니다.

비용 절감, 지속 가능성, 생성형 AI


AWS의 다양한 서비스가 기업에 제공하는 비용 절감 효과, 지속 가능성에 대한 끝없는 도전과 추구, 마지막으로 다양한 생성형 AI 기술을 통한 성장과 혁신. 이 3가지 키워드가 계속해서 강조되었습니다.
본 포스팅에는 위 3가지 측면에 입각하여 2024 AWS Summit Seoul에서 기억에 남았던 부분에 대한 요약과 함께 느낀점 회고 형식으로 작성해 보도록 하겠습니다 :-)


1. 기조 연설

[1] The Frugal Architecture

AWS CTO Werner Vogels는 Frugal Architecture (절약형 아키텍처)의 중요성에 대해 강조하면서 클라우드 환경에서 비용 효율성이 얼마나 중요한지 이야기했습니다. 클라우드 환경은 기업의 하드웨어 제약을 제거하여 서비스의 개발과 배포 속도를 혁신하는데요, 이렇게 클라우드가 가져다 주는 비용 효율성과 지속 가능성을 기업들이 우선순위로 두고 핵심 제약사항으로 고려해야 한다고 힘주어 말했습니다. 그리고 이를 실현하기 위해 아키텍트로서 가져야 할 철학과 접근 방법에는 무엇이 있는지 제시했습니다. 직접 아래와 같이 간단하게 정리해 보았습니다.

  1. 비기능적 요구사항으로서의 비용: 보안 및 규정 준수와 마찬가지로 비용을 비기능적 요구사항으로 보고, 비즈니스 초기부터 이를 아키텍처에 반영해야 합니다.
  2. 비즈니스 비용에 맞는 시스템 설계: 시스템은 수익 창출 요인을 중심으로 설계되어야 하며, 규모의 경제를 통해 시간이 지남에 따라 비용을 줄일 수 있어야 합니다.
  3. 타협과 균형: 아키텍처는 비용, 탄력성, 성능 등의 요소 간의 타협을 통해 균형을 맞추는 것이 중요합니다.
  4. 측정과 관찰: 중요한 지표를 측정하고 회사 모든 구성원이 볼 수 있도록 하여, 측정을 통해 비용을 통제하고 최적화해야 합니다.
  5. 지속적인 최적화: 비용 최적화는 지속적인 질문과 분석을 통해 이루어져야 하며, 작은 최적화가 누적되어야 큰 개선 효과를 볼 수 있습니다.
  6. 끊임없는 도전: 자만하지 말고 끊임없이 변화하고 탐구해야 비용 효율적인 대안을 찾을 수 있습니다. "우리는 이렇게 해왔다"는 위험한 생각을 버리고 새로운 방법을 모색해야 합니다.

AWS CTO의 연설 이후 이어서 우리나라 스타트업 기업 '인프랩(Inflab)' 이동욱 CTO의 이야기를 들어볼 수 있었는데요, AWS 서비스를 어떤 식으로 사용하는지, 다양한 서비스 중에서 상황과 비용 최적화에 적절한 서비스를 어떻게 선택하는지, 사업을 키워 나가는 과정에서 비용이 차지하는 부담을 어떻게 줄이고자 어떤 고민을 하고 어떤 방법을 도입했는지 들어볼 수 있었습니다. 가장 많은 비용을 사용하는 리소스는 무엇인지 지속적으로 기록하고 회고하며 비용을 절감하고 문제를 개선해 나갔다는 이동욱 CTO의 말을 통해 역시 기록의 힘은 어디에서나 통하는구나, 나도 기록을 멈추지 말아야지 생각했어요.
또 일정 시간만 운영하는 서비스의 경우에는 서버리스 서비스가 적절하지만, 24시간 가동될 필요가 있는 서비스는 서버리스보다는 직접 서버를 운영하는 것이 효율적일 수 있다는 이야기가 특히 흥미로웠습니다. AWS의 서버리스 컴퓨팅을 사용하면 서버 프로비저닝, 확장 및 관리의 필요없이 애플리케이션과 서비스를 구축하고 실행할 수 있어 유용한데요. 저는 AWS CCP 자격증 준비를 하면서 AWS Lambda, Fargate, DynamoDB, Kinesis 등 다양한 서버리스 서비스들의 종류와 특징에 대해 파악했었는데, 언제 어떤 식으로 서버리스 서비스를 선택해야 기업에 큰 이득이 될까 항상 궁금했었거든요. 평소 궁금했던 내용에 대한 답변이 되어서 이동욱 CTO의 연설이 특히 귀에 쏙쏙 들어왔습니다. 실제로 기업에서 아마존 웹서비스의 서비스를 어떤 기준으로 선택하고 활용하는지 사례를 통해 들어볼 수 있어서 좋았습니다.


[2] platform engineering

카카오페이증권의 조지훈 실장이 플랫폼 엔지니어링에 대해 발표했습니다. 강의에 따르면 플랫폼 엔지니어링은 협력과 자동화를 통해 사용자 경험을 향상시키고 조직의 생산성을 높이는 데 중점을 둔다고 합니다. 또 사용자와 제품의 관점에서 플랫폼을 디자인하고 개발하여, 비즈니스 경쟁력을 강화하고 확장할 수 있는 다양한 방법을 고려합니다. 특히 클라우드 엔지니어들은 다양한 구성원들과 협업을 통해 플랫폼을 만들어가는 과정을 공유하고, 인프라와 프로세스를 개선하며, 고객의 경험에 집착하며 신뢰를 얻어가는 과정을 즐겨야 한다고 말했습니다.
저는 현재 데이터 사이언스를 공부하며 데이터 사이언티스트로서 성장을 도모하고 있습니다. 하지만 소프트웨어 엔지니어링에도 큰 관심과 뜻이 있어서 고려사이버대학교 소프트웨어공학과에 편입해 공부를 병행하고 있어요. Linux OS, 소프트웨어 공학, 데이터 엔지니어링 등 다양한 분야를 탐구하며 엔지니어로서의 역량도 함께 키우고자 열심히 노력하고 있는데, 조지훈 실장님의 연설을 듣고 나서 데이터에 특화된 클라우드 엔지니어로서도 꿈을 펼쳐보고 싶다는 큰 욕심이 생겼습니다. 특히 저는 문제를 발견하고 정의하고 해결하는 과정을 정말 정말 즐기기 때문에, 고객의 관점에서 문제를 해결할 수 있는 플랫폼 엔지니어, 클라우드 엔지니어로서도 잘 해낼 수 있겠다는 생각이 들었습니다. 계획한 대로 AWS Certified Data Engineer Associate 자격증도 6월에 취득하고, 계속해서 관련 역량을 키워야겠다고 한 번 더 다짐했어요. 소프트웨어는 정말 공부를 하면 할수록 어쩐지 더 멀어지고 깊어지는 망망대해 같다는 생각이 들기도 하지만, 그래서 더 매력적이고 중독적인 분야인 것 같습니다. 


[3] generative AI 

이어서 생성형AI에 대한 연설이 이어졌는데요. AWS Nitro System architecture, Bedrock, Q, Partyrock 등 AWS가 제공하는 다양한 생성형 AI 서비스가 기업에 가져다줄 혁신과 발전, 성장에 대한 큰 자신감을 엿볼 수 있었습니다.
특히 Amazon Bedrock 서비스가 제일 흥미롭더라구요. 검색 증강 생성(RAG)을 위해 Amazon Bedrock의 관리형 지식 기반을 사용하거나 Amazon Bedrock 에이전트를 통해 workflow를 자동화하여 복잡한 과제에 대해 수많은 LLM을 사용할 수 있다는 부분이 인상적이었습니다. CCP 자격증을 공부하면서 배운 바에 따르면 Amazon Bedrock은 서버리스 서비스거든요. 기업이 인프라나 서버를 관리할 필요가 없을 뿐더러, 이미 익숙한 AWS 서비스를 사용하여 생성형 AI 기능을 애플리케이션에 안전하게 통합하고 배포할 수 있는 장점이 있다니, 내가 기업의 수장이고 생성형 AI 기술을 도입하고자 한다면 Bedrock을 써보지 않을 이유가 없을 것 같았어요. 해당 기조연설을 통해 저는 Bedrock에 대해 더 공부할 수 있는 데이터분석 강의를 오후에 듣기로 결정했습니다! 궁금한 게 생기면 바로바로 배워야죠!
기조 연설을 다 듣고난 후, 저는 AWS의 다양한 생성형 AI 기술을 직접 체험해보기 위해 체험 존으로 바로 달려갔습니다.


2. 체험존

[1] AWS 생성형 AI 체험

2024 AWS Summit Seoul에서는 AWS의 다양한 생성형 AI를 직접 체험해볼 수 있었습니다.

내가 고른 테마로 프로필 사진을 생성해주는 AI - 실물 얼굴의 특징이 잘 살아있습니다.
재밌는 중세시대 테마로 갤러리를 만들어주었던 부스 - 저는 브라질 열대우림의 부족 출신이랍니다.
강아지와의 음성 대화를 통해 교감을 하고 점수를 얻는 즐거운 게임! 아쉽게도 3등을 해서 에코백 선물을 받지는 못했습니다. 음성을 꽤나 잘 인식하고 챗봇도 잘 작동해서 재밌었어요.
게임 이후 Quicksight를 이용해서 시각화 대시보드를 만들어 설명해 주셨습니다. 저 진짜 제가 만든 모델로 Quicksight 너무 사용해보고 싶습니다......

이 밖에도 즐거운 체험존이 많았고 다양한 체험에 참여했는데, 사람이 정말정말 많아서 모든 것을 다 찍고 기록하기가 어려웠습니다. 체험존에서는 AWS의 혁신적인 서비스의 극히 일부분만 체험할 수 있었음에도 불구하고 정말 즐거웠습니다. 재밌게 체험에 참여하는 사람들의 웃음소리와 열정적인 분위기가 아직도 기억에 선명합니다. (나도 이런 혁신에 참여할 수 있는 인재가 되길 바라며...)


[2] 기업 부스 체험

기업 부스는 다양하게 체험했는데, 기억에 남았던 서비스 위주로 회고를 작성해 보겠습니다.

설명을 들었던 많은 기업의 서비스 중에서 가장 기억에 남는 1가지를 꼽으라면 kt ds의 AI:ON-U 서비스를 꼽고 싶습니다. AI:ON-U(이하 AI온유)는 코딩이나 소프트웨어 지식이 없는 업무 담당자가 쉽게 생성형AI를 도입할 수 있도록 돕는 서비스 였는데요, 특히 GUI 환경에서 손쉽게 고객상담 챗봇 서비스를 제작하고 배포할 수 있는 예시를 들어 설명을 해주셨던 것이 아주 인상깊었어요. 담당하셨던 직원분께서 설명을 이해가 쏙쏙 잘되게 기깔나게 해주셔서 더더욱 흥미로웠던 것 같습니다. 게다가, kt ds의 AI온유 설명을 들은 이후에 데이터분석 강의(AWS 벡터 데이터베이스 사용 모범사례)를 이어서 들었더니, AI온유의 전체적인 서비스가 어떻게 구성되었는지 더욱 이해가 되고 말았습니다! 내 머릿속에 구체화되는 벡터 데이터와 그 흐름...!!! 검색 증강 서비스(RAG)와 벡터 데이터 베이스....!! (오버 죄송합니다)
챗봇 서비스를 도입하고 싶지만 AI 서비스 개발을 위한 지식과 노하우가 부족하고 전문 인력이나 인프라 구비가 어려운 비즈니스 구성원들에게 AI온유가 시간과 비용을 절감해줄 수 있겠다는 생각이 들었습니다. 제가 생각했을 때 이번 2024 AWS Summit Seoul이 가장 강조하는 3가지 - 비용 절감, 지속 가능성, 생성형 AI - 이 모든 관점에서 고객의 문제점을 해결해줄 수 있는 멋진 서비스라고 생각했어요.
만약 제가 럭키비키하게 해당 기업에 취업하게 된다면 해당 서비스를 구축하는 데 필요한 데이터베이스 관리 및 보안에 참여해 보고 싶습니다. 또, 서비스에 사용하는 다양한 LLM 모델의 API를 관리하는 코드도 작성해 보고 싶어요.


[3] 데이터분석 강의

벡터 데이터베이스 사용 모범사례

해당 강의에서는 생성형 AI에서 벡터 데이터베이스를 어떻게 사용할 수 있는지 그 방법과 모범사례를 통한 전략을 설명했는데요, 다른 많은 강의 중에서 해당 강의를 선택한 이유는 위에서 언급한대로 기조연설을 들으면서 Amazon Bedrock에 관해 큰 흥미가 생겼기 때문이었어요. 머신 러닝 모델을 구성하기 위해서는 굉장히 방대한 양의 데이터 수집과 학습이 필요하고 또 이를 위해 막대한 컴퓨팅 파워와 시간이 필요한데, 이를 서버리스로 관리해주는 서비스가 바로 Amazon Bedrock이거든요. 베드락의 큰 장점 중에 하나가 바로 기존 AWS 환경에 구축해 두었던 RDS, Aurora 등의 데이터베이스를 인터페이스를 통해 활용할 수 있는 것이라고 해서, 이 과정에서 벡터 데이터가 어떻게 활용되는지 자세히 알아보고 싶었어요.
그럼 본격적으로 강의 내용을 한번 쌈빡하게 정리해 보도록 하겠습니다.


[1] 생성형 AI 개요 및 데이터베이스의 역할

생성형 AI는 파운데이션 모델을 기반으로 합니다. Amazon Bedrock은 파운데이션 모델을 사용하여 생성형 AI 어플리케이션을 구축하고 확장할 수 있도록 돕는 서비스입니다. 서버리스이기 때문에 인프라나 서버를 관리할 필요가 없고, API를 사용하여 간단하게 개발을 할 수 있어요. 특히 검색 증강 생성(RAG) 기술을 이용하여 회사의 데이터와 상호작용할 수 있도록 FM을 구성할 수 있다는 것이 엄청난 장점인데요.
우리가 가지고 있는 파운데이션 모델에 어떤 질문을 한다고 가정했을 때, 아직 학습하지 않은 내용을 질문한다면, 파운데이션 모델은 우리가 원하는 답을 찾이 못할 가능성이 있습니다. 이런 단점을 보완하기 위해 파운데이션 모델에 더해 우리가 기존에 가지고 있는 Knowledge Base를 이용해볼 수 있는데요. 만약 Knowledge Base 안에 내가 궁금해했던 데이터가 마침 있다면, 파운데이션 모델과 연동해서 사용해서 원하는 답을 얻을 수 있겠죠. 이게 바로 검색 증강 생성(RAG)의 힘입니다. 즉, 우리 모델을 파인튜닝하거나 추가 학습시키지 않고도 벡터 데이터를 활용해 더 나은 기능을 구현할 수 있게 되는 것이죠.
벡터 임베딩이 사용되는 방법은 다음과 같습니다. Knowledge Base에 pdf 파일이 하나 있다고 가정해 봅시다. 이 파일의 데이터를 여러개의 청크로 나누어 아마존 타이탄과 같은 임베딩 모델에 던져줍니다. 타이탄은 이것을 분석해서 데이터를 벡터화하고, 이 벡터화된 데이터를 빠르게 재사용하기위해 RDS, Aurora와 같은 (벡터베이스 저장이 가능한) 데이터베이스에 저장을 합니다. 이후 사용자가 관련 내용을 검색했을 때, 검색한 내용 역시 벡터화해서 우리의 데이터베이스에서 벡터 서치를 실행하고, 그 결과를 파운데이션 모델에게 벡터로 전달해주게 되면, 그 모델은 마지막으로 자연어 처리를 한 값을 사용자에게 응답하게 되는 것이죠.


[2] 벡터 저장소로서의 PostgreSQL & pgvector 전략

먼저 PostgreSQL은 무료로 사용할 수 있는 오픈 소스 데이터베이스입니다. PostgreSQL은 다양한 데이터 타입과 인덱스 방식을 지원하는데, 확장 기능을 통해 새로운 기능을 손쉽게 추가할 수 있는 뛰어난 확장성을 자랑합니다. pgVector는 PostgreSQL의 확장 기능을 활용하여 벡터 데이터 저장, 인덱싱, 검색 등을 수행합니다.
pgVector는 토스트(TOAST)를 활용한 데이터 저장 최적화 전략인 Storage 전략을 취합니다. TOAST(The Oversized-Attribute Storage Technique)란 보통 기준값(평균 8KB)보다 큰 데이터를 저장하는 메커니즘을 의미하구요. 데이터를 저장하는 최소의 단위를 8KB라는 페이지로 구성하고, 여기에 로우 단위로 데이터가 들어가며, 그 로우의 컬럼이 2KB가 넘어가는 경우에 이것들을 페이지 안에 인라인으로 저장하지 않고 토스트 테이블에 별도로 저장하게 해주는 기능- 정도로 생각하면 됩니다. 토스트를 잘 관리하기 위해서는 테이블의 스토리지 타입을 plain(토스트 없음)으로 고정시키던지, postgress 파라미터 중에서 min_parallel_table_scan_size을 사용하여 더 많은 병렬을 유도하는 방법이 있습니다. 참고로 컬럼 스토리지 타입은 4가지 인데, 토스트가 없는 Plain, 기본값으로 토스트가 있고 임계값이 2KB인 Extended, 임계값 초과 시 toast 테이블에 데이터를 저장하는 External, 테이블에 인라인으로 압축하여 저장하는 Main까지 해서 4개입니다. pgVector의 경우 Plain과 External을 주로 사용합니다.
Storage 전략에서 더 나아가서 pgVector는 유사성 검색을 위한 효율적인 인덱싱을 위한 HNSW 전략, 클라우스 알고리즘을 활용한 인덱싱 방법으로 IVFFlat 전략, 마지막으로 ANN 쿼리를 위한 pgVector 필터링 전략을 취하고 있습니다.


[3] pgVector 로드맵

pgVector에 이제 HNSW용 병렬 구축 생성 기능이 추가되었으므로, 기존보다 더 빠르게 인덱스를 구축할 수 있는 방법이 제공될 것이라고 합니다. 추가적으로 벡터에 스칼라 속성을 결합하여 향상된 인덱스 기반 필터링이 가능하도록 기술 개발에 힘쓰고 있으며, 디멘젼당 더 많은 데이터 유형이 지원되도록 개발이 이루어지고 있다고 합니다.


[4] 강의 회고

복잡한 개념을 명확하게 설명해 주신 점이 정말 좋았습니다. 비록 모든 내용을 100% 이해하고 기록하진 못했더라도, Amazon Bedrock의 혁신성과 pgVector의 발전 방향성에 대해서는 충분히 이해하고 납득할 수 있었습니다. 특히 벡터 임베딩과 RAG(Retrieval-Augmented Generation)과 같은 기술이 추가적인 파인튜닝 없이도 AI 모델을 개선하는 방법에 대한 구체적인 논의가 저에게는 매우 유익했습니다. 또 벡터 데이터를 저장하고 관리하기 위한 PostgreSQL 및 그 확장 기능인 pgVector에 대한 모범 사례 역시 저에게 도움이 되었는데, 이 부분에서 너무 집중하느라 메모를 많이 하지 못해 회고에는 해당 부분을 비중 있게 다루지 못해 아쉽습니다.
본 강의를 통해 (정말 궁금했던) Amazon Bedrock이 대규모 데이터셋과 복잡한 계산을 서버리스로 관리하는 방법에 대해 직관적으로 이해할 수 있었습니다. 이 강의에서 얻은 지식과 방향성을 토대로 앞으로 생성형 AI 및 벡터 데이터베이스 분야를 더 깊이 탐구하고 싶다는 욕심이 생겼어요. 그래서 학생 신분으로는 과연 Amazon Bedrock의 실용적인 응용 프로그램을 어떻게 접하고 탐구해볼 수 있을지 한번 고민해보고자 합니다.


[4] 보너스 tmi

  1. 선착순으로 런치 쿠폰 준다그래서... 부끄럽지만 AWS밥은 무슨 맛일까 너무 궁금해서 아침 7시에 코엑스에 도착했어요 (ㅋㅋㅋㅋㅋ) 8시 땡치자마자 바로 등록하고 런치쿠폰 받았는데, 알고보니 10~11시까지도 계속 나눠주시더라는 슬픈 전설이 있습니다...
  2. 기조연설 전까지 별마당도서관에서 국문이력서랑 영문이력서 열심히 수정해서 코엑스 링코에서 프린트해서 갔는데, 아쉽게도 기업 부스에 이력서를 하나도 못 드리고 왔어요. 제가 용기가 없던 이유 반, 뭔가 그럴 분위기가 아니었던 이유 반을 변명삼아 봅니다. 경력자들이 명함 넣고 가는 통은 종종 있던데 거기에라도 꽂아넣을까 고민했지만 결국 못했습니다... 흑흑.
  3. AWS 자격증 소지자가 사용할 수 있는 작은 라운지에 무료 간식과 인형뽑기가 있었어요. CCP 자격증을 당당하게 보여드리고 과자랑 음료수를 야무지게 챙겼습니다. 정말 뿌듯한 순간이었어요... 같이 동행한 친구가 라운지 밖에서 저를 기다리고 있어서 인형뽑기는 안했는데 다음에는 친구도 꼭 자격증을 따서 함께 인형뽑기 줄을 설 수 있었으면 좋겠습니다!
  4. 참여했던 기업 부스중에 제가 인턴으로 지원했다가 서류 탈락했던 곳이 있었어요. 학생임에도 불구하고 친절하게 서비스 설명을 해주시고 '****에서 미래에 뵙겠습니다!' 라고 말씀해주셔서 은근한 위로가 되었어요. 해당 기업의 공고가 다시 보이면 자기소개서에 이 얘기를 꼭 써야겠어요.
불고기 랩 + 치킨 샌드위치 + 파스타 샐러드 + 견과류 + 훈제란 1구 + 파인애플 주스 + 호두파이.... 진짜 배불렀어요
파인애플 주스 진하고 너무 맛있었구요
특히 이 불고기랩이 진짜 실하고 단짠하니 너무 맛있었습니다. AWS 최고 ㅠㅠ
얼굴보호를 위해 블러처리 했습니다 - 자격증 소지자만 출입 가능했던 certified 존!!! 정말 친절했던 직원분 감사합니다.
야무지게 고구마말랭이 집어먹었습니다


 
이것으로 길었던 2024 AWS Summit Seoul 회고를 마치겠습니다. 만약 처음부터 끝까지 모두 읽어주셨다면 당신은 대단하고 멋진 사람이예요. 감사합니다.

 

7주차 월요일, 드디어 개인 EDA 프로젝트 발표가 있었는데요. 주제 구상 - 데이터 수집, 분석, 시각화 - 인사이트 도출 - 관련 논문 분석 - 모델링 후 논문 검증 - 인사이트 도출 - 발표자료 구성 - 그리고 이 모든 작업을 몇 번이고 되돌아 반복하는 일련의 과정을 통해, 정말 많은 것을 배우고 성장할 수 있었던 기회였습니다.

이번 포스팅에서는 개인 EDA 프로젝트를 준비하면서 스스로 기특하고 자랑스러웠던 점, 그리고 스스로 확인한 나의 강점을 먼저 작성해 보겠습니다. 그리고 이어서 개선할 점을 이어서 적어보겠습니다.


칭찬할 점

첫째, 끊임 없이 피드백을 찾아 나섰습니다.

저희 ASAC 강사님께서는 이번 개인 EDA 발표를 준비하는 25명의 동기들을 모두 혼자서 멘토링하시느라 정말 바쁘셨는데요. 다르게 말씀드리면, 이번 프로젝트에서 강사님 1분을 제외하고는 조언을 구할 다른 멘토가 단 한 분도 계시지 않았습니다. 강사님께서는 몸이 열 개라도 모자라 보이셨고 야근을 자주 하셨어요. 그렇게 원하는 만큼 피드백을 얻기 어려웠던 (나름 열악했던) 상황 속에서, 저는 계속해서 현직자들과 네트워킹을 시도하며 프로젝트에 대한 가이드를 스스로 찾아 나섰습니다. 주로 일일 세미나를 오셨던 강사분들께 부탁을 드렸는데요. 쉬는 시간에 냅다 노트북을 가지고 가서 프로젝트를 보여드리고 조언을 구했습니다. 아니면 이메일이나 링크드인으로 자기 소개를 보내고 강의에 대해 느낀점을 말씀드린 뒤 정중하게 피드백 부탁을 드렸습니다.

쉬운 일은 아니었습니다. 바쁘신 분들께 초면에 그런 부탁을 드린다는 게 생각보다 많은 용기가 필요했어요. 제가 부족한 만큼 정성스럽고 따뜻한 피드백보다는 타격감 있는 직설적인 피드백들을 주로 받게 되었고, 그래서 자존심도 속도 많이 상하기도 했는데요. 그래도 정말 감사한 마음으로 모두 수용했고, 피드백을 찾아 나서는 일을 절대 멈추지 않았습니다. 나 혼자였다면 몰랐거나 흐린눈 하며 내버려 뒀을 어설프고 엉성한 부분들을 열심히 뜯어고치고 보완하면서 프로젝트의 완성도를 올릴 수 있었습니다.

문제상황을 적극적으로 해결하고자 전문가에게 도움을 청할 용기를 낸 나 자신을 정말 칭찬하고 싶습니다. 이번 프로젝트를 통해 네트워킹의 중요성을 다시한 번 확인했습니다. 사람들이 왜 협업을 통해 일을 진행하는지 알 수 있었던 좋은 기회였어요. 앞으로도 이렇게 스스로 배움을 찾아 나서는 적극적인 자세를 통해 더 많이 성장하고 발전하는 제가 되겠다고 다짐했어요. 

둘째, 어려운 과제 속에서 포기하거나 타협하지 않았습니다. 원하는 목표를 달성할 수 있었던 가장 큰 이유였습니다.

저는 25명의 동기들 중에서 이번 개인 EDA 프로젝트에 유일하게 AI 모델링 작업을 포함하여 발표했습니다. 아직 머신러닝 수업을 듣기 전이었지만, 제가 데이터 사이언스와 엔지니어링을 통한 문제해결에 큰 뜻과 욕심이 있다는 걸 아시는 강사님께서 제가 원하는 방향으로 프로젝트를 수립하도록 적극적으로 도와주셨기 때문에 가능한 일이였습니다. 또, CNN을 통한 Image Classification이 프로젝트의 메인 소재였는데, 평소 열심히 Neural Network의 원리를 탐구하고 Tensorflow Keras나 PyTorch와 같은 프레임워크들을 적극적으로 연습하고 실습한 덕분에 원하는 대로 프로젝트를 준비할 수 있었습니다.

딥러닝 과정에서 원하는 목표 성능이 확보 될 때 까지 여러 방법으로 모델을 수정하고 학습시키고 평가하는 일을 수없이 반복했습니다. 마음처럼 일이 풀리지 않아 마음이 흔들릴 때마다 매번 잘 다잡으려고 노력했어요. 그렇게 복잡하지 않고 간단할 거라고 생각했던 것도 예상대로 진행되지 않는 것을 보면서 실무에서는 더 어려운 일이 많이 일어나겠구나 싶었습니다. 여하간, 힘들고 답답할 때마다 타협하지 않고 목표한 일들을 무사히 수행해낸 나의 집념에 박수를 보내고 싶습니다.

앞으로 더 열심히 공부하고 지식 수준을 높여서 더 어렵고 복잡한 모델도 학습시켜 보고 싶습니다. 인공지능은 정말 너무 어려운데, 어려운 만큼 또 너무 재밌어요. 현재 ASAC 과정에서 머신 러닝 모델링 방법들을 여러 가지 배우기 시작했는데요. 평소 몰두했던 인공신경망(Neural Network) 뿐만 아니라 다른 모델링 학습 법도 이렇게 다양했다니 매일이 놀라움의 연속이더라고요. 즐겁게 배우고 있어요. 언제나 새로운 것들을 proactive한 자세로 배우고 탐구하는 제가 되도록 하겠습니다.


개선할 점

데이터 분석의 전문성을 높이기 위해서는 통계학적 지식을 보완할 필요가 있음을 깨달았습니다.

이번 프로젝트는 주제의 특성상 저의 도메인 지식을 적극 활용할 수 있었던 덕분에 통계학적 접근이 크게 필요하지 않았다고 생각했는데요. 그럼에도 불구하고 통계학적으로 분석한 근거가 함께 수반되었다면 더 논리적인 프로젝트 진행이 가능했겠다는 아쉬움이 남습니다. 

저는 지금 고려사이버대학교 소프트웨어공학과 학생으로 이번 3학년 1학기에 확률과 통계 수업을 듣고 있는데요, 수업 내용을 다시 한 번 정리하고 복습해서 다음 프로젝트에 꼭 적용해 보기로 했습니다. 또, 이번 여름 계절 학기에도 통계 관련 수업을 선택해서 연속적으로 수업을 듣기로 했습니다. 앞으로 통계학적 지식을 보충해서  자신뿐만 아니라 다른 사람들 역시 데이터 기반의 의사결정을 내릴 수 있도록 돕는 데이터 전문가가 될 수 있도록 노력하겠습니다.


 

이것으로 첫 번째 프로젝트(개인 EDA) 에 대한 회고 작성을 마치도록 하겠습니다. 개인 프로젝트 깃허브 링크가 궁금하시다면 댓글을 달아주시거나 niceonesuri@gmail.com으로 연락주세요. 감사합니다.

 

 

오늘 포스팅에는 유명한 Kaggle 신용카드 사기 감지 데이터셋(Credit Card Fraud Detection)을 가지고 데이터 전처리/분석/머신러닝을 하는 과정을 기록할 것입니다. 데이터 EDA를 진행하고 적절한 전처리를 해준 후 머신러닝 모델링을 수행하고 성능 지표를 비교하는 일련의 과정을 전부 담을 예정인데요, 의식의 흐름대로 작성할 예정이라 중간 중간 Tmi도 많고 삽질하는 내용까지도 필터링 없이 기록할 것임을 미리 알려드립니다.

 

1. 데이터 불러오기, 컬럼/결측치/데이터 타입 확인

https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud/data

 

Credit Card Fraud Detection

Anonymized credit card transactions labeled as fraudulent or genuine

www.kaggle.com

!kaggle datasets download -d mlg-ulb/creditcardfraud
!unzip "/content/creditcardfraud.zip"

먼저 캐글 신용카드 사기 감지 데이터셋을 다운로드받아서 가지고 옵니다. 저는 API Command를 복사하여 실행하고 코랩에 다운받아진 파일을 unzip해주는 형식으로 간단히 데이터를 불러왔습니다. 이렇게 하면 파일을 직접 다운로드해서 가져오는것보다 훨씬 빠릅니다.

 준비된 데이터프레임에 총 31개의 column, 284807개의 row를 확인했습니다.

card.info()

info 메소드를 통해 컬럼의 데이터타입과 결측치 여부를 확인했는데, 다행히 모든 컬럼에 결측치 없이 데이터가 잘 들어가 있었고, 정수타입의 Class 컬럼을 제외한 모든 열은 float64 타입임을 확인했습니다. 컬럼명에 들어있는 V1~V28의 경우 어떤 속성인지 알 수는 없고, Amount는 해당 row의 결제 금액, Class는 정상/사기 이진 분류(binary classification) 결과(label) 컬럼에 해당합니다.

만약 object 타입의 컬럼이 있었다면 라벨 인코딩(Label Encoding)이나 원핫 인코딩(One-Hot Encoding) 등의 작업을 통해 값을 숫자로 변환하는 작업이 필요합니다. 다행히 모두 숫자로 이루어져 있기 때문에 따로 인코딩 작업은 들어가지 않아도 될 것 같습니다 :-)

또, NaN 등의 결측치가 있는 경우 적절한 근거를 가지고 대표값으로 결측치를 채워 넣거나 해당 row를 삭제하는 등의 작업을 통해 결측치를 제거해 주어야 합니다. 다행히도 우리의 친절한 캐글 신용카드 데이터셋은 우리에게 그런 노가다를 요구하지 않고 있습니다... (흡족)

 

2. 결제 금액 분포도 그래프로 확인

결제 금액을 나타내는 Amount 컬럼의 분포도를 그래프로 확인해 보겠습니다.

import seaborn as sns
import matplotlib.pyplot as plt

plt.style.use('fivethirtyeight')
plt.figure(figsize = (10, 4))
plt.xticks(range(0, 40000, 1000), rotation = 45)
sns.histplot(card['Amount'], bins = 100, kde = True)
plt.show()

Tmi지만 저는 matplotlib의 테마 중에서 fivethirtyeight를 가장 좋아합니다. 왜인지는 모르겠습니다. 그래서 웬만하면 그래프를 그릴 때 fivethirtyeight으로 테마를 변경한 다음에 그래프를 뽑는 편입니다. 이런 각박한 작업 속에서도 내 취향이란 걸 반영할 수 있다는 게 전 재밌더라구요^_^...;;;

plt.figure(figsize = (8, 4))
plt.xlim(0, 1000)
plt.xticks(range(0, 1000, 50), rotation = 45)
sns.histplot(card['Amount'], bins = 500, kde = True)
plt.show()

다시 본론으로 돌아와서... 0부터 약 500달러 미만의 결제 금액이 차지하는 비율이 압도적으로 많은 것을 확인할 수 있었습니다. 이 그래프를 확인한 후 저는

과하게 치우친 Amount 컬럼의 값을 로그변환하여 보정해 주면 모델의 성능 지표가 상승할 것이다

라는 첫 번째 가설을 세우게 됩니다.

 

3. Amount 컬럼 박스 플롯 그리기

결제 금액 분포도를 살펴본 이후 저는 Amount 컬럼의 박스 플롯을 그려서 이상치를 확인해 봐야겠다는 생각이 들었는데요.

sns.boxplot(data = card, y = 'Amount')

쩝... 네... 첫 번째 삽질 보여드립니다. 방금 위에서 데이터가 쏠려 있음을 확인해놓고 박스플롯이 예쁘게 그려지리라고 생각한 제가 좀 바보같네요. 결제 금액의 박스 플롯은 큰 의미가 없는 것 같으니 다음으로 데이터 프레임의 상관 계수를 확인해 보도록 하겠습니다.

 

4. 상관 계수 시각화하기

card.corr()

corr() 메소드를 이용해서 모든 컬럼 사이의 상관계수를 나타내어 보았습니다. 한 눈에 들어오질 않으니 일단 쉐입을 확인해 볼게요...

card_corr = card.corr()
card_corr.shape
# (31, 31)

31개의 컬럼 사이의 상관계수가 (31, 31) 정사각 쉐입의 데이터프레임으로 이쁘게 반환되었습니다. 저는 저렇게 e어쩌고로 나타내진 숫자값들은 봐도 봐도 적응이 안되더라구요. 어쨌든 이 상태로는 전체 분포를 한 눈에 알아보기 힘들기 때문에 seaborn의 heatmap을 이용해서 그래프로 시각화를 해 보도록 하겠습니다.

두 번째 Tmi... 저는 또 이렇게 색상이 중요한 그래프를 그릴 때 괜히 컬러 팔레트 고르느라 1-2분을 더 낭비하는 것을 좋아합니다.. 역시 각박한 일상 속에서 찾아내는 저만의 소소한 작은 행복입니다 (^_^*) 이번에는 따뜻한 색감의 노랑-브릭 계열의 팔레트를 골라 보았습니다.

plt.figure(figsize = (8, 8))
sns.heatmap(card_corr, cmap = "YlOrBr")
plt.tight_layout()
plt.savefig("corr.png")

그래프를 큼직하게 뽑아서 괜히 savefig를 이용해 png파일로 다운로드까지 해 보았습니다. 이것도 자주 안하면 계속 까먹어요. ㅋ

상관관계 히트맵에서 양의 상관관계가 높을수록 색깔이 진한 갈색에 가깝고, 음의 상관관계가 높을수록 연한 노란색에 가깝습니다. 그래프의 변두리에 희끗희끗하게 보이는 밝은 부분들이 음의 상관관계가 높은 지점입니다. 

이 때, 레이블에 해당하는 Class컬럼과 가장 낮은 상관관계를 보이는 컬럼은 V14와 V17인것으로 확인되는데요.

이렇게 레이블과 관계 없는 컬럼의 이상치를 제거하면 모델의 성능 지표가 상승할 것이다

라는 두 번째 가설을 세우게 됩니다. 

 

5. [before] baseline 모델링

(5-1) train, test 구분하고 레이블 비율 확인해 보기

card.drop('Time', axis = 1, inplace = True)

먼저 큰 의미가 없는 Time 컬럼은 drop을 통해 삭제를 해 주고 시작하겠습니다.

def train_test(df):
    card_copy = card.copy()
    X_features = card_copy.iloc[:, :-1] # label에 해당하는 마지막 'Class'컬럼 제외
    y_label = card_copy.iloc[:, -1]     # label에 해당하는 마지막 'Class'컬럼만

    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X_features,
                                                        y_label,
                                                        test_size = 0.2,
                                                        random_state = 1004,
                                                        stratify = y_label)
    
    return X_train, X_test, y_train, y_test

X_train, X_test, y_train, y_test = train_test(card)

card 데이터프레임의 마지막 'Class' 컬럼을 기준으로 feature과 label을 구분했구요. 싸이킷런의 train_test_split을 통해 8:2의 비율로 train과 test를 분할했습니다. 3번째 Tmi...인데.. 저는 random_state로 항상 1004(천사)를 사용합니다. ^^;;;;;

len(X_train), len(X_test), len(y_train), len(y_test)
# (227845, 56962, 227845, 56962)

이렇게 나눠졌구요. 그럼 데이터에서 레이블 비율(0과 1의 비율)이 어떻게 되는지 살펴볼까요?

y_train.value_counts(normalize = True).apply(lambda x: str(round(x * 100,4)) + '%')
y_test.value_counts(normalize = True).apply(lambda x: str(round(x * 100,4)) + '%')

와우! 사기에 해당하는 1번 레이블이 약 0.17%에 해당하는 극소수로 확인되었습니다. 하긴, 사기 거래가 2~30%씩 차지하고 있으면 그것도 말이 안되겠네요. 저는 이렇게 뭐든 시각화하면서 가지고 있는 데이터와 친숙해지는 작업이 꼭 필요하다고 생각합니다. 그렇지 않으면 의미 없는 숫자놀음에 그치게 된다고 생각해요,,, 어쨌거나 저쨌거나 이 비율 확인 후 저는 

평가 지표(metrics)로 Accuracy가 아닌 다른 지표들을 전부 다 확인할 필요가 있음

이라는 결론을 내렸고, 다양한 평가 지표를 모두 확인해 보기로 결정했습니다.

(5-2) 모델 학습 후 예측 성능 평가

def train_eval(model, f_tr = None, f_test= None, t_tr= None, t_test= None):
    # 모델 학습
    model.fit(f_tr, t_tr)

    # 예측 
    pred = model.predict(f_test)
    pred_proba = model.predict_proba(f_test)[:, 1]

    # 평가 (1) confusion matrix 출력
    from sklearn.metrics import confusion_matrix
    confusion = confusion_matrix(t_test, pred)
    print("----- confusion matrix -----")
    print(confusion)
    
    # 평가 (2) accuracy / precision / recall / f1 / roc_score 출력
    from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
    accuracy = accuracy_score(t_test, pred)
    precision = precision_score(t_test, pred)
    recall = recall_score(t_test, pred)
    f1 = f1_score(t_test, pred)
    roc_auc = roc_auc_score(t_test, pred_proba)
    print("----- evaluation score -----")
    print(f"accuracy : {accuracy:.4f}")
    print(f"precision : {precision:.4f}")
    print(f"recall : {recall:.4f}")
    print(f"f1 : {f1:.4f}")
    print(f"ROC_SCORE : {roc_auc:.4f}")

 

먼저 baseline 모델을 학습시키고 예측을 수행한 다음 confusion matrix와 여러 평가 지표를 출력하는 함수를 작성했습니다.

from lightgbm import LGBMClassifier
from xgboost import XGBClassifier

lgbm = LGBMClassifier(n_estimators = 1000,
                      num_leaves = 64,
                      n_jobs = -1,
                      boost_from_average = False)

xgb = XGBClassifier(n_estimators = 1000,
                    learning_rate = 0.05,
                    max_depth = 3, 
                    eval_metric = 'logloss')

이후 XGBM, LightGBM 모델 2가지를 학습하고 예측 평가를 수행했습니다.

train_eval(lgbm, X_train, X_test, y_train, y_test)
train_eval(xgb, X_train, X_test, y_train, y_test)

(좌) LGBM / (우) XGB

결과값은 위와 같이 나왔는데요. Accuracy는 대체로 동일하고, precision과 recall, f1은 LightBGM 모델이 더 높았고, roc_auc 점수는 XGB 모델이 더 높게 측정되었습니다.

 

6. [after] 가설 2가지 적용하기

이 번엔 데이터 분석을 통해 세웠던 2가지의 가설을 검증해보는 작업을 수행해 보겠습니다.

레이블과 관계 없는 컬럼의 이상치를 제거하고 Amount 컬럼의 값을 로그변환하여 보정한다면 모델의 성능 지표를 상승시킬 수 있을 것이다

(6-1) 이상치 찾아내기

def find_outliers(df, column):
    # 해당 컬럼의 사기 데이터 series 선언
    frauds = df[df['Class'] == 1][column]
    
    # IQR 구하기
    import numpy as np
    Q1 = np.percentile(frauds.values, 25)
    Q3 = np.percentile(frauds.values, 75)
    IQR = Q3 - Q1

    # 1QR에 1.5를 곱해서 최댓값, 최솟값 구하기
    lowest_value = Q1 - IQR * 1.5
    highest_value = Q3 + IQR * 1.5

    # 이상치 데이터 찾아서 인덱스만 리스트로 뽑아내기
    outliers = frauds[(frauds < lowest_value) | (frauds > highest_value)].index

    # 이상치 인덱스 리스트 리턴
    return outliers

데이터 프레임과 원하는 컬럼명을 인자로 받아서 해당 컬럼의 이상치 인덱스를 리스트로 반환하는 함수를 작성했습니다 :) 처음부터 바로 함수를 작성한 건 아니고, 컬럼 하나를 지정해서 하나하나 실행해본 다음 깔끔하게 함수로 정리하는 작업을 통해 함수를 완성했습니다.

find_outliers(card, "V14")
# Index([8296, 8615, 9035, 9252], dtype='int64')

find_outliers(card, "V17")
# Index([], dtype='int64')

아까 히트맵을 통해 확인했을 때 Class컬럼과 가장 낮은 상관관계를 보이는 컬럼은 V14와 V17인것으로 확인되었었는데요, find_outliers 함수를 통해 확인해 본 결과 V14 컬럼의 경우 이상치가 4개 발견되었고, V17 컬럼의 경우 이상치가 발견되지 않았습니다 :)

 따라서 저는 V14 컬럼의 이상치 데이터 4개를 삭제해 보겠습니다.

 

(6-2) 이상치 제거, 로그 변환 후 train, test 분리

def train_test_no_outliers(df):
    card_copy = df.copy()
    # outlier 제거
    outliers = find_outliers(card_copy, "V14")
    card_copy.drop(outliers, axis = 0, inplace = True)

    # Amount 컬럼 로그변환
    import numpy as np
    card_copy['Amount'] = np.log1p(card_copy['Amount'])
    
    # 데이터셋 나누기
    X_features = card_copy.iloc[:, :-1] # label에 해당하는 마지막 'Class'컬럼 제외
    y_label = card_copy.iloc[:, -1]     # label에 해당하는 마지막 'Class'컬럼만

    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X_features,
                                                        y_label,
                                                        test_size = 0.2,
                                                        random_state = 1004,
                                                        stratify = y_label)
    
    return X_train, X_test, y_train, y_test

X_train_af, X_test_af, y_train_af, y_test_af = train_test_no_outliers(card)

train, test 분리하는 함수에 Amount 컬럼 로그변환과 outlier rows 제거하는 과정을 추가한 다음 분리 작업을 실시했습니다.

(6-3) 모델 학습 후 예측 성능 평가

train_eval(lgbm, X_train_af, X_test_af, y_train_af, y_test_af)
train_eval(xgb, X_train_af, X_test_af, y_train_af, y_test_af)

(좌) LGBM / (우) XGB

다음과 같이 결과가 나왔습니다.

 

7.  모델 4개 비교

지금까지 학습시킨 모델들의 성능 지표를 표로 작성하여 한눈에 비교해 보겠습니다.

  Baseline(XGB) Baseline(LGBM) After(XGB) After(LGBM)
Accuracy 0.9996 0.9996 0.9996 0.9996
Precision 0.9419 0.9405 0.9405 0.9302
Recall 0.8265 0.8061 0.8061 0.8163
F1 0.8804 0.8681 0.8681 0.8696
ROC Score 0.9853 0.9863 0.9882 0.9793

저는 이상치 제거와 로그 변환의 전처리 과정을 거친 다음 학습한 After 모델들의 모든 성능 지표가 다 개선될 것이라고 생각했습니다. 하지만 실습 결과 Precision / Recall / F1의 경우 오히려 Baseline의 수치가 더 높게 나온것을 확인할 수 있었습니다. 역시 인생이란 건 그렇게 호락호락하지 않습니다. 원하는 대로 결과가 안 나와서 좀 찝찝하긴 한데요...

다만, After XGB 모델 ROC-AUC 스코어의 경우 Baseline 모델들보다 점수가 더 상승한 것을 확인할 수 있었습니다. 이번에 다룬 Kaggle 신용카드 사기감지 데이터셋의 경우, 기존에 미리 확인한 대로 전체 데이터의 약 0.17%만이 사기에 해당하는 극도로 치우친 값을 가지고 있었습니다. 이렇게 과하게 편향된 데이터셋의 경우 ROC-AUC 스코어가 유의미하므로, 만약 4가지 모델 중 한 가지를 골라야 한다면 저는 ROC-AUC 스코어가 가장 높게 나온 After(XGB) 모델을 고르는 것도 나쁘지 않겠다는 생각을 했습니다.

 

8. 마무리

이번 포스팅은 여기에서 실습을 마치도록 할텐데요. 위의 4가지 모델에서 성능 지표를 더 올리기 위해서는 0.17%에 해당하는 사기 데이터를 펌핑해서 수를 늘려 주는 방법을 사용하면 좋을 것 같습니다. 마음같아서는 지금 당장 하고싶지만 할일이 많아서 일단 여기에서 끊겠습니다만, 좀 찝찝하기 때문에 시간여유가 생기면 모델 개선 작업을 추가로 수행해서 추가 포스팅을 하도록 하겠습니다.

이번 실습을 통해서 앞으로 다가올 머신러닝 팀프로젝트에 어떤 식으로 데이터 EDA를 진행하고 모델링을 개선해나갈 수 있을 지 좋은 가이드라인이 되었습니다. 

읽어주셔서 감사합니다 :-)

 

 

사이킷런의 프레임워크와 연동할 수 있는 전용 XGBoost 래퍼 클래스에는 분류용 XGBoostClassifier, 회귀용 XGBoostRegressor이 있습니다. 래퍼 클래스는 다음과 같은 장점을 가지고 있습니다.

  • 사이킷런의 기본 estimator를 그대로 상속해 만들었기 때문에 fit()과 predict()만으로 학습과 예측이 가능합니다.
  • GridSearchCV, Pipeline 등 다른 사이킷런의 다른 유틸리티를 그대로 함께 사용할 수 있습니다.
  • 기존의 다른 프로그램의 알고리즘으로 XGBoost 래퍼 클래스를 사용할 수도 있습니다.

 

https://smartest-suri.tistory.com/40

 

ML | 파이썬 XGBoost API 사용하여 위스콘신 유방암 예측하기

XGBoost란?트리 기반의 앙상블 학습에서 가장 각광받고 있는 알고리즘 중 하나로, 캐글(kaggle) 등 경연 대회에서 입상한 많은 데이터 사이언티스트들이 XGboost를 사용하면서 널리 알려지게 되었습니

smartest-suri.tistory.com

 

이전 글에서 학습했던 기본 XGBoost API 대신 사이킷런 연동 XGBoost 래퍼 클래스 XGBoostClassifier를 사용해 모델을 학습시키고 예측을 수행해 보겠습니다.

 


 

 

호출 및 hyperparameter

from xgboost import XGBClassifier
xgb_wrapper = XGBClassifier(n_estimators = 400, # num_boost_round -> n_estimators
                            learning_rate = 0.05, # eta -> learning_rate
                            max_depth = 3, 
                            eval_metric = 'logloss')

 

learning_rate와 같이 기존 사이킷런 하이퍼 파라미터와의 호환성 유지를 위해 변경된 하이퍼 파라미터들이 있으므로 유의합니다.

 

 

 

학습 및 예측

fit()과 predict() 메소드를 이용해서 모델을 학습시키고 예측을 수행해 보겠습니다.

# 학습
xgb_wrapper.fit(X_train, y_train, verbose = True)

# 예측
w_preds = xgb_wrapper.predict(X_test)
w_preds[:10]
# array([1, 0, 1, 0, 1, 1, 1, 1, 1, 0])

w_pred_proba = xgb_wrapper.predict_proba(X_test)[:, 1]
w_pred_proba[:10]
# array([8.8368094e-01, 2.7957589e-03, 8.9875424e-01, 1.8748048e-01,
#        9.9204481e-01, 9.9990714e-01, 9.9954444e-01, 9.9904817e-01,
#        9.9527210e-01, 1.9664205e-04], dtype=float32)

 

아주 빠르고 간단하게 학습과 예측을 수행했습니다.

 

 

평가

이전 포스팅에서 작성해 둔 get_clf_eval() 함수를 이용해서 사이킷런 래퍼 XGBoost로 만들어진 모델의 예측 성능 평가를 해 보겠습니다.

get_clf_eval(y_test, w_preds, w_pred_proba)

지표 이전 이후
Accuracy 약 0.96 약 0.97
Precision 약 0.97 약 0.97
Recall 약 0.97 약 0.98
F1-Score 약 0.97 약 0.98
ROC-AUC 약 0.99 약 0.99

 

이전 실습보다 평가 지표가 조금 상승했습니다. 이번 실습에서는 early stopping을 따로 설정하지 않은 관계로 train 데이터를 train과 valid 데이터로 나누는 과정을 생략하였고, 그래서 트레인 데이터셋의 수가 늘어난 영향이 있을 것으로 파악됩니다. (애초에 트레이닝 데이터가 풍부한 데이터셋은 아닌 관계로)

 

 

여기까지 XGBoost 관련 두 번째 실습을 마쳐봅니다. 감사합니다 :-)

 

XGBoost란?

트리 기반의 앙상블 학습에서 가장 각광받고 있는 알고리즘 중 하나로, 캐글(kaggle) 등 경연 대회에서 입상한 많은 데이터 사이언티스트들이 XGboost를 사용하면서 널리 알려지게 되었습니다. 대체로 분류에 있어서 뛰어난 예측 성능을 보이는 특징을 가지고 있습니다.

 

XGboost는 GBM에 기반하고 있는데요. GBM보다 빠르게 학습이 가능하고 오버핏팅 규제 부재 문제 등을 해결한다는 장점이 있다고 합니다. 그 밖에도 Tree pruning이 가능하여 더 이상 긍정 이득이 없는 분할을 가지치기 해서 분할 수를 더 줄이는 추가적인 장점, 자체 내장된 교차 검증 기능, 결손값을 자체 처리할 수 있는 기능 등의 장점도 가지고 있습니다.

 

XGBoost API 학습을 위해 위스콘신 유방암 데이터로 실습을 진행해 본 결과를 정리하여 공유하고자 합니다 :-)

 

 

 

sklearn dataset 위스콘신 유방암 데이터 불러오기

sklearn의 자체 내장 데이터인 load_breast_cancer을 불러오겠습니다.

import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')

dataset = load_breast_cancer()
features = dataset.data
labels = dataset.target

cancer_df = pd.DataFrame(data = features, columns = dataset.feature_names)
cancer_df.head(3)

cancer_df['target'] = labels
cancer_df.head(3)

 

데이터프레임 마지막에 target 컬럼을 추가하여 각 row의 label을 0 또는 1로 나타냈습니다.

dataset.target_names
# array(['malignant', 'benign'], dtype='<U9')

cancer_df['target'].value_counts()
# target
# 1    357
# 0    212
# Name: count, dtype: int64

 

label이 무엇이 있는지 확인해 보니, 0은 malignanat, 1은 benign임을 확인할 수 있었고, 각각 212개와 357개가 있는 것을 확인할 수 있었습니다.

 

 

train, valid, test 데이터셋 나누기

X_features = cancer_df.iloc[:, :-1]
y_label = cancer_df.iloc[:, -1]

 

먼저 target 컬럼을 제거한 X_feature 데이터프레임, target 컬럼만 떼어낸 y_label 데이터프레임을 생성합니다.

# 8:2 비율로 train:test 나누기
X_train, X_test, y_train, y_test = train_test_split(X_features, y_label, 
                                                    test_size = 0.2, 
                                                    random_state = 156)
                                                    
# 9:1 비율로 다시 train:valid 나누기
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train, 
                                            test_size = 0.1, 
                                            random_state = 156)

 

train_test_split() 메소드를 이용하여 8:2 비율로 train:test 데이터셋을 나누고,

나누어진 train은 다시 한 번 9:1 비율로 train:valid으로 나누었습니다.

len(X_tr), len(X_val), len(y_tr), len(y_val), len(X_test), len(y_test)
# (409, 46, 409, 46, 114, 114)

X_tr.shape, y_tr.shape
# ((409, 30), (409,))

 

train, valid, test 데이터의 개수와 shape 등을 확인해 보았습니다.

 

 

 

DMatrix

파이썬 래퍼 XGBoost는 전용 데이터 객체인 DMatrix를 사용합니다. DMatrix의 주요 입력 파라미터는 data와 label입니다.

  • data : 피처 데이터 세트
  • label : (분류) 레이블 데이터 세트 / (회귀) 숫자형인 종속값 데이터 세트
dtr = xgb.DMatrix(data = X_tr, label = y_tr)
dval = xgb.DMatrix(data = X_val, label = y_val)
dtest = xgb.DMatrix(data = X_test, label = y_test)

 

 

Hyperparameter

딕셔너리 형태로 하이퍼 파라미터를 설정합니다.

params = {
    'max_depth' : 3,  # 트리의 최대 깊이는 3
    'eta' : 0.05,     # 학습률 0.05
    'objective' : 'binary:logistic',
    'eval_metric' : 'logloss'
}

num_rounds = 400       # 부스팅 반복 횟수는 400회

 

 

XGBoost 모델 학습

  • early_stopping_rounds : 더이상 지표 개선이 없을 경우에 횟수를 모두 채우지 않고 중간에 반복을 빠져 나올 수 있도록 하는 탈출 도구 (조기 중단, 얼리스탑핑)
  • evals : 평가용 데이터 세트, [(학습튜플), (평가튜플)] 형태 - 반복마다 지정된 평가용 데이터 세트에서 eval_metric의 지정된 평가 지표로 예측 오류를 측정 
xgb_model = xgb.train(params = params,
                      dtrain = dtr,
                      num_boost_round = num_rounds,
                      early_stopping_rounds = 50, # 50번째부터 얼리스탑핑 가능
                      evals = [(dtr,'train'), (dval,'eval')])

400회를 다 채우지 못하고 250회에서 중단되었습니다

 

예측 수행

predict() 메소드를 이용해서 test 데이터의 결과를 예측해 보겠습니다.

pred_probs = xgb_model.predict(dtest)

 

예측한 결과를 다양하게 살펴봅시다. (초반 10개값만 봅시다.)

pred_probs[:10]
# array([9.3829882e-01, 3.6695320e-03, 7.5020140e-01, 4.9393266e-02,
#        9.8045909e-01, 9.9958366e-01, 9.9899417e-01, 9.9919862e-01,
#        9.9767953e-01, 5.2314228e-04], dtype=float32)

 

어떤 값인지 한 눈에 보이지 않으므로 np.round()를 이용해서 살펴보겠습니다.

np.round(pred_probs[:10])
# array([1., 0., 1., 0., 1., 1., 1., 1., 1., 0.], dtype=float32)
# 일의 자리까지 반올림

np.round(pred_probs[:10], 3)
# array([0.938, 0.004, 0.75 , 0.049, 0.98 , 1.   , 0.999, 0.999, 0.998,
#       0.001], dtype=float32)
# 소수 셋째 자리까지 반올림

 

반올림을 해서 보니까 예측한 값이 0과 1 사이의 값으로 도출되었음을 확인할 수 있었습니다. 

이제 예측 확률이 0.5보다 크면 1, 그렇지 않으면 0으로 최종 예측값을 결정하여 preds 리스트에 저장하겠습니다.

preds = [1 if x > 0.5 else 0 for x in pred_probs]
preds[:10]
# [1, 0, 1, 0, 1, 1, 1, 1, 1, 0]

 

 

get_clf_eval() 함수

모델을 평가할 수 있는 함수를 작성해 보겠습니다.

from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix, f1_score, roc_auc_score

def get_clf_eval(y_test, pred = None, pred_proba = None):
    confusion = confusion_matrix(y_test, pred)
    accuracy = accuracy_score(y_test, pred)
    precision = precision_score(y_test, pred)
    recall = recall_score(y_test, pred)
    f1 = f1_score(y_test, pred)
    roc_auc = roc_auc_score(y_test, pred_proba)
    print('Confusion Matrix')
    print(confusion)
    print(f'accuracy : {accuracy}, precision : {precision:.4f}, recall : {recall:.4f}')
    print(f'f1 : {f1}, roc_auc : {roc_auc}')
get_clf_eval(y_test, preds, pred_probs)

  • 정확도 accuracy 약 0.96
  • 정밀도 precision 약 0.97
  • 재현율 recall 약 0.97
  • F1-스코어 0.97
  • ROC-AUC 약 0.99

평가 지표가 굉장히 좋네요 :-)

 

 

XGBoost 내장 시각화 기능 수행하기

XGBoost의 plot_importance() API피처의 중요도를 막대그래프 형식으로 나타냅니다.

  • 기본 평가 지표로 f스코어를 기반으로 해당 피처의 중요도를 나타냅니다.
  • f스코어는 해당 피처가 트리 분할 시 얼마나 자주 사용되었는지를 지표로 나타낸 값입니다.
  • xgboost 패키지는 plot_importance()를 이용해 바로 피처 중요도를 시각화할 수 있습니다.
  • plot_importance() 호출 시 파라미터로 앞에서 학습이 완료된 모델 객체 및 matplotlib의 ax 객체를 입력하면 됩니다.
import matplotlib.pyplot as plt
%matplotlib inline

fix, ax = plt.subplots(figsize = (10, 12))
plot_importance(xgb_model, ax = ax)
plt.tight_layout()
plt.savefig('plot.png')

 

 

 

여기까지 XGBoost API 실습 포스팅이었습니다 :-)

감사합니다.

 

2024년 5월 4일 (토) AWS CCP (Certified Cloud Practitioner) 자격증 시험에 응시해서 한 번에 합격했습니다 :-)

준비 이유, 공부 과정, 합격 후기 3가지 테마로 나누어 회고를 작성해 보고자 합니다.

 


 

1. 응시한 이유

현업에서 클라우드 기반의 업무를 하는 곳이 많다는 것을 알게 된 이후로 줄곧 클라우드에 대한 호기심을 가지고 있었습니다. 하지만 학교에서 일하던 저에게는 클라우드에 대한 기초지식도 경험도 전무했습니다. 갈수록 클라우드가 뭔지 점점 더 궁금했습니다. 그래서 배우기로 했습니다. 궁금한 게 있으면 배워버리면 그만 아니겠습니까?

 

가장 배우고 싶었던 건 아마존 웹서비스(AWS)였는데요. 아마존 웹서비스의 클라우드 환경에 대해 전반적인 지식을 습득하고, 어떤 기능과 서비스가 있는지 파악하고 싶었습니다. 그리고 CCP 자격증이 그러한 저의 목적에 가장 부합한다는 것을 알게 되었습니다.

 

https://aws.amazon.com/ko/certification/certified-cloud-practitioner/

 

AWS Certified Cloud Practitioner 자격증 | AWS Certification | AWS

이 자격증을 취득하면 AWS 클라우드, 서비스 및 용어에 대한 높은 수준의 이해가 입증됩니다. 이 자격증은 IT 배경이 아닌 사람이 클라우드로 전환하는 후보자가 클라우드 경력을 쌓을 수 있는 입

aws.amazon.com

 

2. 공부 과정

저는 다음과 같이 3가지 방법으로 이번 시험을 준비했습니다.

1. Udemy 강의를 들으며 AWS 구조, 서비스, 기능 등에 대한 이해와 Hands-On 실습

 

제가 들은 강의는 <[NEW] Ultimate AWS Certified Cloud Practitioner CLF-C02> 입니다. 처음엔 '15시간이면 짧네'라고 생각하고 듣기 시작했는데요. 실제로 체감되는 강의 시간은 그보다 훨씬 길었습니다. 특히 저는 모든 실습을 따라했고 따로 정리까지 해가며 들어서 진도가 더 더뎠는데요. 강의를 다 듣는 데 꼬박 3주가 걸렸습니다. 다른 일이나 공부를 병행하지 않았다면 2주면 충분했을 것 같습니다.

 

말씀드린 대로 저는 자격증 취득 뿐만 아니라 정말 AWS에 대해 제대로 알고 싶었기 때문에 강의를 들었고, 대체적으로 만족합니다. 프랑스 액센트라던지 강사의 쩝쩝대는 소리 등이 썩 유쾌하지는 않았기 때문에 이 강사의 다른 강의를 또 듣진 않을 것 같습니다. 그래도 강의 구성이나 내용 면에서는 무척 만족하고 있어요. 강의를 완강한 이후로 내가 AWS에 어떤 서비스가 있고, 언제 어떤 서비스를 이용할 수 있을지 파악하고 있다는 자신감이 생겼습니다.

 

다만 강의가 영어로 진행되므로 영어에 대한 거부감이 있으시면 해당 강의를 비추드립니다. 또 저처럼 AWS 서비스에 대한 이해가 목적이 아닌 빠르게 자격증 취득만 목표로 하고 계신 분, 어느정도 기초 지식이 있으신 분들께서도 굳이 강의를 들으실 필요는 없을 듯합니다. 무엇보다 배울 의지가 없는 분들께서는 해당 강의를 절대 끝까지 완강하지 못하실 것이라고 생각합니다.

 

(*) 강의는 할인 가격으로 17,000원에 구매했습니다.

 

2. AWS Skill Builder 웹사이트의 AWS Cloud Practitioner Essentials Course를 반복 학습하며 서비스별 특징과 기능에 대한 이해 강화

기본 : 클릭  / 한국어 자막 : 클릭

 

스킬 빌더 웹사이트의 에센셜 코스는 CCP 준비하는 사람이라면 반드시 봐야 하는 필수 코스라고 생각합니다. 정말 추천합니다. AWS에서 준비한 만큼 설명이 아주 깔끔하고 친절하게 잘 돼있습니다.

 

"설명을 읽고 복습 -> 서비스별 중요 포인트 기능과 컨셉 암기 -> 확인 문제 풀면서 복습 -> 마지막 모의고사 풀면서 복습" 하는 과정을 3회독 반복하고 났더니, 시험을 통과할 수 있겠다는 자신감이 더욱 up up 되었습니다. 따로 유데미나 강의를 안 들으시는 분들께서는 반드시 에센셜 코스의 모든 내용을 숙지하고 암기하시면서 시험을 준비하시기 바랍니다.

 

아, 저는 영어로 봤는데요. 한국어 번역도 매끄럽게 잘 돼있는지는 모르겠습니다만, 영어 문장도 어렵지 않게 되어 있으니 한번 영어로 공부하는 것도 도전해 보시길 바랍니다. 

 

3. Udemy 모의고사를 2회독하면서 이해가 부족한 점을 찾아서 보완

 

마지막으로 부족한 부분은 Udemy 모의고사 <6 Practice Exams | AWS Certified Cloud Practitioner CLF-C02> 를 여러 번 풀면서 보완했습니다. 1회독 할 때는 문제를 풀면서 모르는 개념을 찾아서 외워가면서 풀었구요, 오답노트를 따로 정리했습니다. 2회독 할 때는 실전 느낌으로 풀었습니다. 좀 지저분한 문제들도 많긴 하지만 그래도 대체적으로 실전이랑 비슷한 느낌이라고 생각합니다. 1회독 할 땐 너무 어려운거 아냐? 싶었는데 2회독 하니까 적정 난이도로 느껴지더라고요. 유데미 모의고사를 70% 이상으로 패스할 수 있는 수준이라면 실제 시험도 무리 없이 합격하실 수 있을 거예요.

 

(*) 할인 가격으로 17,000원에 구매했습니다.

 

3. 합격 후기

 

 

시험이 끝나자마자 바로 합격/불합격 결과를 확인할 수 있어서 속이 무척 시원했습니다. 시험 당일 밤에 축하 합격 메일이 왔고, 성적표와 자격증 pdf를 다운받을 수 있었습니다. 이렇게 포트폴리오에 한 줄 더 추가할 수 있게 된 점이 무척 기쁘지만, 무엇보다도 클라우드 경험이 없었던 저에게 AWS라는 방대한 플랫폼을 이해하고 수많은 서비스를 파악할 수 있는 능력이 생겼다는 것이 정말 뿌듯합니다. 

 

공부하면서 가장 궁금하고 써보고싶었던 서비스들을 몇 개 정리해 보면 다음과 같은데요.

  1. Rekognition
  2. Quicksight
  3. DynamoDB
  4. EMR
  5. CloudTrail
  6. Lambda, Eventbridge

위 서비스들을 맘껏 사용하고 다룰 수 있는 곳에 취직하면 얼마나 행복할까... 그런 생각을 해 봅니다. (ㅎㅎ)

 

마지막으로, 저는 이어서 AWS Certified Data Engineer - Associate 자격증을 준비하려고 합니다. 5월 말, 6월 초 취득을 목표로 하고 있습니다. 끊임없이 배우고 발전하는 스스로가 되겠다고 오늘도 다짐하며, 자격증 시험 합격 회고 작성을 마무리 하겠습니다.

 

 

감사합니다 :-)

 

 

 

SK플래닛 T아카데미 ASAC 빅데이터 분석, AI 과정 5주 차 회고를 작성합니다.

 

 

지난 5주차에는 셀레니움을 이용해서 웹크롤링 하는 방법, 태블로 프로그램을 이용해 데이터 시각화하는 방법, pandas를 이용해서 데이터 정제 하는 방법을 다루었습니다. 셀레니움과 태블로를 저는 처음 사용해 봤기 때문에 해당 수업을 특히 집중해서 들었고 정말 재밌게 실습에 참여했습니다. 수업이 끝나고 따로 원하는 사이트를 정해서 혼자서 셀레니움 복습도 해 보고, 공공데이터를 받아 태블로 시각화도 복습해 보면서 감을 조금씩 익혔습니다. 네이버 쇼핑 데이터를 크롤링하다 차단을 당하는 웃지 못할 해프닝도 있었습니다. 다양한 방법을 동원해 봤는데 결국 안 풀리더라고요(^^;;;) 개인 프로젝트 주제로 네이버 쇼핑 데이터가 필요했었다면 큰 낭패를 보았을 뻔했습니다. 휴!

 

 

지난 주부터 드디어 본격적인 개인 EDA 프로젝트 준비에 돌입했습니다. 많은 동기들이 주제를 오래 고민했지만 저는 그중에서도 특히 갈피를 잡지 못해 많이 헤맸던 것 같습니다. 다른 사람들은 어떻게 주제를 선정하나 살펴보니, 먼저 본인이 취업하고 싶은 분야를 정하고, 그 분야에 어필할 수 있는 방향으로 주제를 세부화해 나가시더라고요. 예를 들면 나는 쿠팡에 취직을 하고 싶으니 쿠팡의 자체 브랜드에 관해 조사한다던지, 화장품에 관심이 많으니 화장품 성분과 리뷰의 상관관계를 조사한다던지 하는 것이죠. 저의 경우에는 '취업하고 싶은 분야'랄 게 딱히 없었기 때문에 문제가 되었습니다. 여기에도 관심이 있고 저기에도 관심이 있다고 할까요. 내가 모르는 새로운 어떤 분야에든 데이터 사이언티스트로 진출해서 열린 마음으로 일해보고 싶다!는 오픈 마인드였는데, 이게 개인프로젝트 주제 선정에는 도움이 되지 않더라고요.

 

 

결론부터 말씀드리면 저는 제가 지난 20대를 몸담아 바친 교육계와 관련된 주제를 선정하게 되었습니다. 사실 저는 이쪽 주제를 선정하는 것은 처음부터 약간 기피했습니다. 개인 EDA 프로젝트를 통해 완전히 새로운 분야에 진출하는 초석을 다질  욕심이 있었기 때문인데요. 프로젝트를 어느정도 완성해 가는 지금 생각해 보면 결국 제가 잘 아는 도메인을 선정한 것이 올바른 결정이었다는 생각이 듭니다. (물론 만약 특정 분야에 큰 관심이 있었더라면 그 분야를 연구하는 게 낫겠지만요.) 제가 잘 아는 분야인 만큼 연구의 방향성이 눈에 더 잘 보이기도 했고, 인사이트가 꼬리에 꼬리를 물고 확장되어감을 느낄 수 있었기 때문입니다. 

 

 

저의 개인 프로젝트 흐름은 다음과 같습니다.

  • 2022 개정 교육과정 분석, 관련 데이터 수집 분석, 관련 연구 논문 여러 개 분석, 직접 AL/ML 모델링 후 현황 연구 검증, 앞으로의 연구 방향성 제시

걱정까진 아니지만 그래도 아직 고민하고 있는 부분은, 다른 동기들의 수집 데이터가 대부분 구매 패턴 데이터, 후기 데이터 등 유저에 관련된 데이터들이더라고요. 저도 적당히 크롤링을 하긴 했지만 상대적으로 국가 문서와 연구 논문 분석이 메인을 이루기 때문에 이런 부분에서 데이터가 빈약해 보이지 않을까 하는 생각이 들었습니다. 또, 취업 측면에서 본다면 확실히 고객 관련 데이터를 분석하는 것이 기업의 매출 신장과 직결되기 때문에 더 어필될 것도 맞을 테고요. 주제에 필요한 데이터가 다르긴 하지만 그래도 약간의 조바심이 나는 것은 사실이기 때문에, 발표 전에 시간이 남으면 관련된 유저 데이터도 한번 검색해서 분석해보고자 합니다.

 

 

그래도 차별화 되는 점은 제 프로젝트에 인공지능 모델링을 직접 수행하고 연구 논문을 검증하는 과정이 포함되었다는 것인데요. 제가 알기론 다른 동기들 중에 딥러닝을 이번 개인 EDA 프로젝트 소재로 삼으신 분은 없는 걸로 알고 있습니다. 아직 아삭 과정에서 배우지 않은 파트이기도 하고요. 다만 저는 인공지능 엔지니어링 쪽에 관심이 많아 이전부터 독학을 해오고 있던지라 경험과 기본지식을 가지고 있는 상태였고, 강사님께서 딥러닝 쪽에 뜻이 있다면 이렇게 이렇게 해보자며 코칭도 해 주셔서, 큰 용기를 내어 프로젝트에 포함을 시키게 되었습니다. 그리고 아주 잘한 결정이라고 생각해요. 확실히 프로젝트가 빠른 성장에 도움이 됩니다. 주제에 관련된 국내외 논문을 이것저것 검색하고 직접 읽고 모델링을 해서 검증까지 해보게 되면서 크고 작은 산을 정말 많이 넘어야 했고, 남의 코드도 많이 들여다봐야 했거든요. 역시 사람은 고난과 역경을 극복하면서 발전하는 것 같습니다. 

 

 

아, 생각보다 해외 연구 논문 읽는 게 재밌더라고요. 외국인 친구들이랑 가깝게 잘 지내고 영어 원서도 한 달에 한 권씩 읽고 강의도 유데미 해외강의로만 들으면서 영어공부를 꾸준히 한 것이 큰 도움이 되나 봅니다. 연구 논문에 나오는 많은 용어들 전부 평소에 제가 공부하면서 쓰고 익히던 것들이라 읽고 이해하는 데 막힘이 없어서 뿌듯했습니다. 취업할 때 이런 점을 잘 어필해 봐야겠습니다. 다들 영어 공부 열심히 하시면 언제든 이렇게 도움이 된다는 점! 기억하시고 꾸준히 공부하시면 좋겠습니다.

 

 

개인 프로젝트가 끝나면 AWS CCP 자격증 시험이 곧바로 있는데요, 그 자격증 시험이 끝나면 저는 Kaggle 대회 참여를 시작해보려고 합니다. 케글 케글 말로만 들었지 실제로 써본 적은 아직 없었는데 이번 프로젝트 준비하면서 정말 케글에 지겹도록...(ㅋㅋㅋㅋㅋㅋ) 많이 접속해야 했거든요. 어떤 식으로 사이트가 구성되고 운영되고 내가 써먹을 수 있는지 적당히 잘 알았어요. 동기들 중에 딥러닝에 관심이 있는 분이 계시다면 스터디를 구성해 보는 것도 좋은 방법일 것 같습니다.

 

 

다음 회고는 개인 EDA 프로젝트가 끝난 다음 작성해 보도록 하겠습니다. 감사합니다 :)

 

 

 

 

 

한동안 코테를 잠시 안풀었더니

효율성 테스트를 통과하는데 조금 헤맸던 문제입니다..!

 

Stack 알고리즘을 활용하지 않고 문제를 풀었을 때 테스트 케이스 정확성은 통과하는 데 문제가 없으나, 효율성 테스트를 통과하기 어려우실 수 있습니다. 효율성 테스트까지 통과할 수 있는 문제 풀이 방법을 알려드릴게요 :)

 

 

https://school.programmers.co.kr/learn/courses/30/lessons/12909

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제 상황 : 괄호가 바르게 짝지어졌다는 것은 '(' 문자로 열렸으면 반드시 짝지어서 ')' 문자로 닫혀야 한다는 뜻입니다. 예를 들어

"()()" 또는 "(())()" 는 올바른 괄호입니다.
")()(" 또는 "(()(" 는 올바르지 않은 괄호입니다.


'(' 또는 ')' 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.


 

1.

먼저 빠른 탈출을 위해 다음 4가지 경우에는 함수를 False로 얼리 리턴하도록 설정해주겠습니다.

  • 주어진 문자열이 ")" 로 시작하는 경우
  • 주어진 문자열이 "(" 로 끝나는 경우
  • 주어진 문자열의 길이가 홀수인 경우
  • 주어진 문자열이 ")" 또는 "(" 한 종류로만 이루어진 경우
def solution(s):
    if s[0] == ")" or s[-1] == "(" or len(s) % 2 == 1 or len(set(s)) == 1:
        return False

 

 

 

2.

다음으로 stack 리스트를 선언해주도록 하겠습니다.

    stack = []

 

우리는 stack 리스트에 "(" 열기 괄호만 넣어줄 것입니다.

왜 열기 괄호만 넣어줄까요? 예시를 통해 한번 확인해 봅시다.

 

문자열 s = "(((())"가 있다고 합시다. 딱 봐도 열기 괄호는 4개인데 닫기 괄호는 2개여서 짝이 맞지 않는다는 것을 알 수 있습니다. 우리는 for루프를 통해 0번째 자릿값부터 차례대로 돌면서 해당 자리의 값이 "("이면 stack에 넣어주고, ")"이면 stack에 있는 열기괄호를 하나 빼 줄 것입니다.

    for char in s:
        if char == '(':
            stack.append(char)
        elif char == ')':
            if not stack:
                return False
            stack.pop()

s = "(((())"

  1. 첫 번째 자리 "(" -> stack = "(" 
  2. 두 번째 자리 "(" -> stack = "(", "("
  3. 세 번째 자리 "(" -> stack = "(", "(", "("
  4. 네 번째 자리 "(" -> stack = "(", "(", "(", "("
  5. 다섯 번째 자리 ")" -> stack이 비어있지 않으므로 stack.pop()
    -> stack = "(", "(", "("
  6. 여섯 번째 자리 ")" -> stack이 비어있지 않으므로 stack.pop()
    -> stack = "(", "("
    if stack:
        return False
    else:
        return True

 

이터레이션을 다 돌고 났을 때

stack이 비어 있으면 짝이 맞으므로 True

stack에 남은 값이 있다면 짝이 맞지 않는 것이므로 False를 리턴합니다.

 

결국 "("의 갯수와 ")"의 갯수가 동일한지 확인하는 것과 다름 없습니다. 다른 방법으로도 정확성 문제는 통과하는 데 어려움이 없으나, 효율성에 문제가 생기는 관계로 stack 알고리즘을 통해 효율성을 높여준 것이라고 보시면 되겠습니다.

 

 

3.

마지막으로 한 번에 코드를 보겠습니다.

def solution(s):
    if s[0] == ")" or s[-1] == "(" or len(s) % 2 == 1 or len(set(s)) == 1:
        return False

    stack = []
    for char in s:
        if char == '(':
            stack.append(char)
        elif char == ')':
            if not stack:
                return False
            stack.pop()
    
    if stack:
        return False
    else:
        return True

 

 

감사합니다!

+ Recent posts