LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


AI-Text 필터링 모델을 위한 거대 ML 모델 적용기

Tech-Verse 2022에서 김형락 님이 발표한 AI-Text 필터링 모델을 위한 거대 ML 모델 적용기 세션 내용을 옮긴 글입니다.

안녕하세요. 현재 Data & Machine Learning 조직에서 NLP 엔지니어로 일하며 AI-Text 필터 모델을 담당하고 있는 김형락입니다. 먼저 제가 가져온 그림을 하나 감상하겠습니다.

제가 그렸다고 하기엔 너무 잘 그린 그림인데요. 사실 이 그림은 AI에 'beautiful forest'라는 입력을 넣고 받은 AI가 그린 그림입니다. AI의 성능이 이와 같이 발전한 배경에는 거대 모델이 있는데요. 이번 글에서는 AI-Text 필터 모델에 거대 모델을 적용해서 성능을 개선한 경험을 공유하겠습니다.

글은 AI-Text 필터가 무엇이고 어떤 문제가 있어서 거대 모델을 사용했는지 먼저 설명한 뒤, 거대 모델을 훈련할 수 있는 기술이 무엇인지 살펴보고, 거대 모델을 활용해서 AI-Text 필터를 고도화한 경험을 공유하는 순으로 진행하겠습니다.

AI-Text 필터란

AI-Text 필터는 LINE의 모니터링 시스템에서 사용하는 필터입니다. 모니터링 시스템은 LINE의 공개 서비스 채널에 들어온 사용자의 문장을 일반적인 문장과 그렇지 않은 문장으로 분류합니다(사용자가 공개한 내용이 아닌 개인 대화 내용 등은 모니터링하지 않습니다). 일반적이지 않은 문장으로는 개인 정보 관련 문장, 성 관련 문장, 괴롭힘 관련 문장, 불법적인 내용이 들어간 문장, 광고 등이 있는데요. 만약 입력된 문장이 일반적이지 않은 문장으로 판단되면 모니터링 요원이 직접 확인 후 조치합니다.

현재 월평균 약 3억 8천만 건 정도의 문장이 모니터링 시스템으로 유입되는데요. AI-Text 필터 모델이 정확하지 않다면 모니터링 요원이 더 많은 문장을 직접 확인해야 하기 때문에 더욱 많은 모니터링 리소스가 필요합니다. 따라서 AI-Text 필터는 더 정확할수록 좋습니다.

AI-Text 필터를 고도화하려면 단일 언어로 돼 있는 수많은 공개 사전 학습(public pre-training) 모델 중 하나를 선택한 뒤 파인 튜닝(fine tuning) 단계에서 AI-Text 필터 작업에 맞게 훈련시켜야 합니다. 문제는 수많은 공개 사전 학습 모델 중 어떤 모델의 성능이 제일 좋은지 찾아야 한다는 점과, 기존 언어에서 다른 언어로 변경했을 때 또다시 어떤 모델이 성능이 좋은지 찾아야 한다는 점입니다. 이 과정은 비용이 많이 들 수밖에 없습니다.

이 문제를 해결하기 위해 세 가지 솔루션을 적용했습니다. 우선 언어가 달라졌을 때 발생하는 문제를 해결하기 위해 다국어 언어를 지원하도록 모델을 확장했습니다. 다음으로 일반적으로 단일 언어 모델보다 성능이 좋지 않은 다국어 모델의 성능을 높이기 위해 거대 모델을 도입했습니다. 스케일링 법칙에 따라서 많은 데이터로 모델 크기를 늘려 학습하면 지속적으로 성능이 좋아지기 때문입니다. 마지막으로 거대 모델을 훈련시킬 수 있는 기술을 도입했습니다.

기존에 서비스하고 있던 단일 언어 모델은 파라미터 수가 1억 1천만 개였는데요. 이를 100배로 키워서 파라미터 수가 110억 개인 다국어 모델로 확장해서 개발과 서비스에 들어가는 여러 가지 비용을 줄였습니다. 다국어 모델로 바꿨기 때문에 여러 언어로 서비스를 확장할 수 있게 됐으며, 그 과정에서 거대 모델을 훈련시킬 수 있는 기술도 확보했습니다.

또한 이번 프로젝트를 진행하면서 LINE에서 MLOps를 담당하고 있는 MLU 팀과 협력해서 거대 모델을 AI-Text 필터 모델에 적용한 뒤 성능을 높이며 고도화할 수 있었고, MLU 서빙 팀과 협력해서 모델 서빙까지 가능하게 만들었습니다. 그럼 어떤 기술로 거대 모델을 훈련시켰는지 살펴보겠습니다.

거대 모델 훈련 기술 소개

거대 모델을 다룰 수 있는 기술은 크게 스케일링 기술과 경량화 기술로 나뉩니다. 스케일링 기술은 거대 모델의 정확도를 그대로 유지하면서 모델을 병렬로 훈련시키는 기술입니다. 컴퓨팅 자원이 굉장히 많이 필요하다는 특성이 있습니다. 경량화 기술은 거대 모델의 정확도를 최대한 유지하면서 모델을 압축시키는 게 목표입니다.

AI-Text 필터는 모델의 정확도가 중요했기 때문에 스케일링 기술을 활용했습니다. AI-Text 필터에 적용한 스케일링 기법을 하나씩 살펴보겠습니다.

첫 번째는 데이터 병렬화 기술입니다. 데이터 병렬화 기술은 여러 데이터가 있을 때 동시에 여러 데이터를 각 GPU에 있는 모델에서 학습하는 방법입니다. 동시에 여러 데이터를 병렬로 학습하기 때문에 학습 속도가 빠릅니다.

두 번째는 모델 병렬화 기술입니다. 대표적인 기술로 'Intra Operator 병렬화' 기술이 있는데요. 한 레이어의 모델 파라미터를 나눠서 각 지표를 병렬로 훈련시키는 기술입니다. GPU를 병렬로 활용하기 때문에 더 효율적으로 사용할 수 있습니다. 최종 산출물을 만들 때는 각 GPU에 쪼개져 있는 레이어 파라미터를 하나로 합쳐주는 All Reduce라는 GPU 통신을 이용해서 하나의 산출물로 모델 결과를 출력합니다.

마지막으로 CPU 오프로드(offload)라는 기술이 있습니다. 모델 파라미터를 CPU 공간으로 옮겨 놓았다가 필요할 때 GPU 공간으로 옮겨서 훈련시키는 기법입니다. CPU 자원까지 확장해서 사용할 수 있기 때문에 모델 크기를 더 키울 수 있다는 장점이 있습니다.

AI-Text 필터에 거대 모델을 적용할 때에는 위 세 가지 기술을 모두 사용했습니다. 그런데 이런 기술을 모두 직접 구현하려면 개발 시간이 굉장히 많이 필요합니다. 이에 개발 속도를 높이기 위해 거대 모델을 훈련시킬 수 있는 프레임워크를 찾아봤고, GPT-3 정도의 모델을 훈련시킬 수 있으면서 CPU 오프로드 기술을 사용할 수 있는 DeepSpeed 프레임워크 오픈소스를 선택했습니다. 이 프레임워크를 이용해 빠르게 거대 모델을 활용할 수 있었습니다.

거대 모델을 적용한 AI-Text 필터 모델 구조

아래는 거대 모델을 적용한 AI-Text 필터 모델의 전체 구조를 나타낸 그림입니다. 저희 환경에 맞게 DeepSpeed 프레임워크를 설정한 후 멀티 노드를 구성했고 각 노드는 GPU 8개로 구성했습니다.

훈련할 때는 실험을 쉽게 진행하기 위해서 훈련 설정 파일을 정의했습니다. 공개 사전 학습 모델은 파라미터가 110억 개인 다국어 모델을 활용했고, AI-Text 필터 작업에 맞게 파인 튜닝했습니다. 전체 훈련에 사용한 데이터는 약 73만 개입니다.

트러블 슈팅 사례

DeepSpeed 프레임워크를 이용해 거대 모델을 도입하는 과정에서 세 가지 문제를 만났습니다. 첫 번째는 DeepSpeed 프레임워크를 환경에 맞도록 설정하는 문제였고, 두 번째는 멀티 노드를 구성할 때 GPU 가속 파일을 공유해야 하는 문제였습니다. 마지막 세 번째는 공개 사전 학습 모델 코드가 병렬화돼 있지 않아서 파인 튜닝 단계에서 병렬화를 사용할 수 없었던 문제였습니다. 각 문제를 자세히 살펴보고 어떻게 해결했는지 설명하겠습니다.

DeepSpeed 프레임워크 환경 설정 문제

환경 설정이 어려웠던 이유는 다음과 같습니다. 먼저 DeepSpeed 프레임워크를 설정하기 위해서 필요한 라이브러리의 의존성이 굉장히 복잡했고, DeepSpeed 프레임워크가 CPU와 GPU 자원을 모두 활용하기 위해서 OS 시스템 라이브러리와 깊이 연관돼 있었습니다. 또한 DeepSpeed 프레임워크에서는 GPU 가속화를 위한 CUDA 익스텐션 파일이라는 것을 자체적으로 만드는데요. 이때 필요한 라이브러리 버전을 G++/C++ 컴파일러와 DeepSpeed 프레임워크, Ninja 빌드 시스템의 각 라이브러리 버전과 맞춰줘야 했습니다.

이 문제를 해결하기 위해 먼저 OS 수준에서 라이브러리를 설정한 뒤, DeepSpeed 프레임워크를 설정하고, 마지막으로 멀티 노드를 구성하기 위한 라이브러리를 설정했습니다.

환경 설정 문제를 해결해 DeepSpeed 프레임워크를 사용할 수 있게 되면서 크게 세 가지 장점을 얻었습니다. 먼저 기존에 훈련시키기 위해 사용하고 있던 라이브러리를 모두 사용할 수 있게 됐습니다. 또한 더 안정적인 DeepSpeed 프레임워크를 활용할 수 있게 됐고, MLU에서 지원있는 Anaconda와 같은 기능을 모두 사용할 수 있게 됐습니다.

이와 같은 장점을 LINE 내 모두가 누릴 수 있도록 Docker 이미지로 만든 뒤 전체 설정 절차를 문서로 만들었습니다.

멀티 노드 구성 시 CUDA 익스텐션 파일 공유 방법 문제

단일 노드 환경에서 훈련시킬 때는 가장 먼저 GPU 가속화를 위한 CUDA 익스텐션 파일을 만들고 이를 기반으로 훈련을 가속화합니다. 그렇다면 멀티 노드 환경에서는 어떻게 훈련시킬까요?

멀티 노드로 확장하면 헤더 노드에서 먼저 훈련을 시작한 뒤 각 워커 노드에 훈련이 시작됐다는 신호를 주는 방식으로 병렬로 진행합니다. 그런데 이때 헤더 노드에만 CUDA 익스텐션 파일이 생성되기 때문에 멀티 노드 훈련이 제대로 수행되지 않는 문제가 발생합니다. 단순하게 '각 서버에 들어가서 CUDA 익스텐션 파일을 생성하면 되는 것 아니냐?'고 생각할 수도 있는데요. 워커 노드 수가 늘어날 수록 점점 비효율적인 방법이 될 것입니다.

이를 해결하기 위해 파일 공유 모듈을 만들었습니다. 훈련이 시작되고 CUDA 익스텐션 파일이 생성되면 파일 공유 모듈에서 각 워커 노드의 IP 주소를 참조해 생성된 파일을 전송합니다.

전체적인 그림으로 보면, 헤더 노드에 CUDA 익스텐션 파일이 생성되면 워커 노드 수와 상관없이 공유 모델이 각 워커 노드에게 공유합니다. 이런 방법으로 멀티 노드를 훈련할 수 있도록 자동화했습니다.

병렬화가 적용되지 않은 공개 사전 학습 모델 문제

세 번째로 사전 학습 모델 코드가 병렬화를 사용할 수 없다는 문제가 있었습니다. 아래 왼쪽 그림을 보겠습니다. 앞서 설명한 Intra Operator 병렬화를 사용하려면 각 모델을 일일이 코딩해야 하는데요. 일반적으로 공개 사전 학습 모델은 병렬화 코드가 구현돼 있지 않기 때문에 파인 튜닝 단계에서 병렬화를 사용할 수 없었습니다.

컨버팅 알고리즘 구현 및 적용

이 문제를 해결하기 위해 파인 튜닝 단계에서 공개 사전 학습 모델을 병렬화할 수 있게 만드는 컨버팅(converting) 알고리즘을 만들었습니다.

컨버팅 알고리즘의 첫 번째 역할은 공개 사전 학습 모델의 코드를 병렬화할 수 있게 구현하는 것입니다. 두 번째 역할은 병렬화할 수 있게 된 모델 코드에 맞게 모델을 로드하기 위해 이미 학습된 공개 사전 학습 모델 파라미터를 자동으로 파티셔닝하는 것입니다.

모델 코드를 병렬화하기 위해서는 모델의 어떤 부분을 병렬화해야 하는지 알아야 하기 때문에 공개 사전 학습 모델을 분석하며 병렬화할 수 있는 부분을 찾았습니다. 아래 왼쪽 그림과 같이 일반적인 트랜스포머는 인코더와 디코더로 구성되고 각 레이어 로직은 거의 동일한데요. 아래 오른쪽 그림과 같이 각 레이어에서 병렬화할 세 부분을 추출했습니다. 첫 번째는 Multi Head Attention의 Key, Query, Value 부분이고, 두 번째는 Multi Head Attention 결과를 계산하는 Feed Forward Network, 마지막으로 최종 결과를 출력하는 Intermediate Feed Forward Network입니다. 이 세 가지 레이어 로직을 병렬화할 수 있게 구현했습니다.

병렬화할 수 있게 만든 부분을 조금 더 자세히 살펴보겠습니다. 먼저 사용하려는 사전 학습 모델과 같은 로직으로 모델 코드를 구현했습니다. 각 모델 레이어에서 병렬화를 적용할 때에는 조금 더 쉽게 테스트하기 위해 GPU를 2개로 설정했습니다. 병렬화는 GPU를 최대한 활용할 수 있는 Intra Operator 병렬화를 적용했습니다. 아래 그림과 같이 Multi Head Attention에서는 각 Key, Query, Value 부분과 Feed Forward Network에 병렬화를 적용했고, 마지막에 잔차 연결(residual connection)을 위해서 각 GPU에 있는 파라미터를 All Reduce했습니다.

Intermediate Feed Forward Network에서도 앞서와 마찬가지로 병렬화 수행 후 최종 계산 결과를 All Reduce하도록 구현했고, 최종 결과가 다시 다음 레이어의 입력으로 들어가도록 설계했습니다. 또한 모델이 수렴 가능하게 각 레이어의 병렬화 구조를 구현하기 위해서 NVIDIA의 Megatron ML 논문을 참고해 만들었습니다. 재미있는 점은 실험에서 모델 구조에 따라 훈련 속도와 성능 차이가 많이 난다는 것을 직접 확인해 볼 수 있었다는 것인데요. 결론적으로 병렬화 코드 설계가 중요하다고 말씀드리고 싶습니다.

모델을 병렬화하고 나면 기존에 병렬화돼 있지 않은 모델의 파라미터를 파티셔닝해서 모델을 로드해야 합니다. 이를 위해 모델 병렬화를 사용하는 GPU 개수와 맞춰서 자동으로 파티셔닝되도록 구현했고, 마지막으로 병렬화가 적용된 파인 튜닝을 진행했습니다.

전체 과정을 다시 정리해 보겠습니다. 병렬화가 적용되지 않은 공개 사전 학습 모델을 병렬화할 수 있도록 컨버팅했고, 모델 코드를 병렬화한 뒤 모델 파라미터를 파티셔닝해서 모델을 올리도록 구현했습니다. 결과적으로 공개 사전 학습 모델이 병렬화돼 있지 않더라도 병렬화 컨버팅 알고리즘을 통해서 문제를 해결할 수 있었습니다.

파인 튜닝에 모델 병렬화 적용 결과 분석

모델 병렬화를 파인 튜닝에 적용한 후 결과를 분석해 봤습니다. 단점부터 말씀드리면 병렬화가 적용되지 않았던 공개 사전 학습 모델을 병렬화할 수 있도록 만들었기 때문에 모델 수렴이 불안정한 모습을 보였고 모델 성능이 조금 떨어졌습니다. 반대로 장점은 공개 사전 학습 모델 코드가 병렬화돼 있지 않아도 파인 튜닝 단계에서 병렬화 훈련이 가능해지면서 더 큰 공개 사전 학습 모델을 학습할 수 있게 됐습니다. 이 장점과 단점은 서로 트레이드오프 관계이기 때문에 조금 더 연구가 필요합니다.

추가로 거대 모델을 적용하고 나서 성능 튜닝을 진행할 수 있도록 알고리즘을 구현했습니다. 알고리즘은 레이블간 상관관계를 반영할 수 있도록 설계했고, Global Correlation Embedding Layer(전체 레이블 간에 어떤 관계가 있는지 파악하는 레이어)를 사용해서 각 레이블을 예측할 때 이전 레이블들과의 상관관계를 학습시킬 수 있도록 구현했습니다.

다음으로 모델 서빙 부분입니다. 모델을 서빙할 때는 크기를 줄이고 추론 속도를 향상하는 등 최적화하는 것이 중요합니다. 보통 거대 모델은 파인 튜닝할 때 모델 파라미터를 부동 소수점 32에서 16으로 줄여서 훈련하는데 이와 같이 파라미터를 줄이면 성능이 떨어질 수 있습니다. 이를 막기 위해 로스 스케일링(loss scaling)이라는 기법을 추가로 활용해 파인 튜닝을 진행해서 기존 파티셔닝 모델의 거의 반으로 크기를 더 줄일 수 있었습니다.

또한 추론 속도를 최적화하기 위해서 GPU 계산에 필요한 커널을 튜닝했고, GPT 값이 크고 제로샷(zero-shot, 학습 과정에서 배우지 않은 작업을 수행하는 것) 작업이 있는 모델도 처리할 수 있도록 추론할 때 병렬화를 지원했습니다. 마지막으로 최적화한 추론 모델을 MLU 서빙 플랫폼에 서빙할 수 있도록 만들었으며, 추론 최적화 부분은 DeepSpeed 프레임워크를 활용했습니다.

모델 비교 실험 결과

이제 실험 결과를 공유하겠습니다. 비교할 모델은 총 세 가지로 알고리즘을 튜닝한 다국어 거대 모델과 튜닝하지 않은 다국어 거대 모델, 현재 서비스하고 있는 AI-Text 필터 모델입니다.

실험에 사용한 데이터는 약 11만 개로, 아래 오른쪽 그림을 보면 데이터 레이블이 굉장히 불균형하게 분포돼 있는 것을 알 수 있습니다. 실제 데이터 도메인 분포와 유사하다고 생각하면 됩니다. 보통 일반적인 데이터가 일반적이지 않은 데이터 레이블보다 훨씬 많은데요. 아래 데이터에서는 특히 괴롭힘에 해당하는 레이블과 불법적인 부분을 나타내는 레이블이 적은 것을 알 수 있습니다.

먼저 레이블 구성이 불균형할 때 성능을 측정할 수 있는 F1 Score로 모델을 비교했습니다. 아래 그래프에서 파란색 막대는 거대 모델을 튜닝한 버전이고, 하얀색 막대는 튜닝하지 않은 버전, 회색 막대는 현재 서비스하고 있는 모델입니다. 거대 모델을 적용하면 전반적으로 모든 레이블에서 성능이 향상되는 것을 확인할 수 있습니다. 또한 튜닝한 버전이 튜닝하지 않은 버전보다 성능이 더 좋은 것도 확인할 수 있습니다. 특히 괴롭힘이나 불법적인 내용의 레이블에서 거대 모델을 적용한 모델의 성능이 더욱 크게 향상됐는데요. 이 결과만 봐도 데이터가 불균형하게 구성돼 있을 때 거대 모델을 도입하면 성능이 얼마나 향상되는지 확인할 수 있습니다.

위 오른쪽 그래프는 전체를 평균 낸 그래프입니다. 현재 서비스하고 있는 모델은 거대 모델을 튜닝한 버전보다 약 9.9% 정도 성능이 떨어졌고, 튜닝하지 않은 버전도 거대 모델 튜닝 버전보다 1% 정도 성능이 낮았습니다.

다음으로 모델의 정확도를 확인할 수 있는 AUC Score를 비교한 결과 그래프입니다. 역시 거대 모델을 적용했을 때 전체적으로 성능이 좋아지는 것을 확인할 수 있습니다. 오른쪽 전체 평균 그래프를 보면 튜닝한 버전보다 현재 서비스하고 있는 모델은 약 9.1% 정도, 튜닝하지 않은 버전은 약 1% 정도 성능이 낮았습니다.

추가로 모델의 정성 평가도 진행했습니다. 정성 평가에 사용한 데이터는 코로나 상황 때문에 금전적으로 어려움을 겪는 사람들에게 대출을 해준다는 내용인데요. 불법 레이블에 속하는 내용입니다. 이 문장을 잡아내려면 문장이 어떤 의미인지 모델이 이해해야 하는데요. 현재 서비스하고 있는 모델을 평가해 보니 약 12%의 확률로 불법이라고 예측했습니다. 문장 전체를 잘 이해하지 못한 것으로 볼 수 있는데요. 반면 거대 모델을 도입하니 99%의 확률로 불법이라고 예측했습니다.

거대 모델 적용 후 기대 효과

이제 실제 서비스에 거대 모델을 적용한 AI-Text 필터를 도입했을 때 어떤 효과를 기대할 수 있을지 살펴보겠습니다. 먼저 앞서 말씀드렸듯 다국어 모델이기 때문에 다양한 언어로 서비스를 확장할 수 있고, 다국어 모델인데도 성능까지 향상되며, 거대 모델을 학습시킬 수 있는 기술도 확보할 수 있습니다.

구체적으로 서비스 부분에서 기대할 수 있는 효과를 살펴보겠습니다. 아래는 거대 모델로 성능을 향상시킨 AI-Text 필터 모델을 모니터링 시스템에 적용했을 때의 기대 효과를 산출한 수치입니다(AI-Text 필터 성능이 10% 향상되면 모니터링 비율이 0.3% 떨어진다는 것을 기준으로 삼았습니다). 한 달에 평균 3억 8천만 건 정도 데이터가 들어올 때, 기존 모델은 모니터링 비율이 1.5%이기 때문에 570만 개 정도의 데이터를 매달 모니터링해야 합니다. 이때 거대 모델을 도입하면 모니터링해야 할 데이터가 456만 개로 줄어듭니다. 봐야 할 데이터가 110만 개가량 줄어드는 것입니다.

아래는 위 결과를 바탕으로 1년 치 모니터링 데이터를 계산한 결과입니다. 거대 모델을 도입하면 모니터링 데이터 개수가 1,300만 개가량 줄어드는 것으로 나오며, 이를 통해 모니터링 리소스를 20% 가까이 줄일 수 있을 것으로 예상합니다.

마치며

거대 모델을 훈련시키고 서빙하기 위한 많은 기술을 이해하고 적용하는 것은 굉장히 어려웠지만, 어려웠던 만큼 거대 모델을 실제 AI-Text 필터 모델에 적용해서 성능을 향상시키는 작업은 무척 재미있었습니다. 또한 거대 모델을 도입하려면 같은 팀은 물론 다른 팀과도 적극적으로 협력하는 것이 중요하다는 사실을 깊이 깨달았습니다.

이번 작업 과정에서 실험할 때 하이퍼 파라미터 튜닝을 하는 것이 굉장히 어려웠는데요. 이를 어떻게 조금 더 효과적으로 진행할 수 있을지 연구가 더 필요하다고 느꼈습니다. 추후 관련 연구 결과를 공유할 수 있기를 바라며 이만 글을 마치겠습니다. 긴 글 읽어주셔서 감사합니다.