{ "cells": [ { "cell_type": "markdown", "id": "4ef9f047", "metadata": {}, "source": [ "# HuggingFace Transformers를 이용한 NLI 모델 학습\n", "\n", "* 본 노트북에서는 klue/roberta-base 모델을 kakaobrain에서 제공하는 KorNLI 데이터셋을 활용하여 모델을 훈련하는 예제입니다. \n", "\n", "* KorNLI 데이터셋은 [kakaobrain](https://github.com/kakaobrain/KorNLUDatasets?fbclid=IwAR0LX_jem7qb6HUikflO-F6lPpfoefK9Yc0jSQIdSKdkX4s8SW1UvoVGc7I)에서 제공하는 데이터셋이며, KLUE의 NLI 데이터셋 보다 데이터 크기가 더 커서 시간이 오래 걸립니다. \n", "\n", "* 모든 소스 코드는 [huggingface-notebook](https://github.com/huggingface/notebooks)을 참고하였습니다.\n", "\n", "* 본 노트북의 학습 내용 대부분은 Huffon님의 [klue-transformers-tutorial](https://github.com/Huffon/klue-transformers-tutorial)을 참고하였습니다.\n", "\n", "* 학습을 통해 얻어질 klue-roberta-base-kornli 모델은 입력된 두 문장의 추론 관계를 예측하는데 사용할 수 있게 됩니다.\n", "\n", "* 노트북의 환경은 ainize workspace 입니다. [ainze](https://ainize.ai/) 홈페이지 오른쪽 상단의 github로 아이디 생성 후 My space에서 workspace를 생성할 수 있습니다. 제가 사용한 workspace의 GPU 환경은 tesla V100 32GB입니다.\n", "\n", "\n", "## 노트북 환경 설정\n", "\n", "모델을 학습시키기 전에 자신의 노트북 환경을 확인하고 필요한 라이브러리를 설치합니다." ] }, { "cell_type": "code", "execution_count": 1, "id": "9ddee489", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "import torch\n", "use_cuda = torch.cuda.is_available()\n", "print(use_cuda)" ] }, { "cell_type": "markdown", "id": "68700205", "metadata": {}, "source": [ "모델을 학습시킬 때 GPU를 사용해야 학습을 빠르게 할 수 있으므로 학습시키기 전 자신의 노트북 환경에서 GPU가 잘 동작 하는지 확인을 합니다. True가 출력이 된다면 학습할 때 GPU가 동작하는 것이고 False가 나오면 동작하지 않는 것입니다. False가 출력이 되신다면 검색을 통해 GPU를 활성화 시키는 방법을 찾아 활성화 시키시면 됩니다." ] }, { "cell_type": "code", "execution_count": 2, "id": "49766327", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[name: \"/device:CPU:0\"\n", "device_type: \"CPU\"\n", "memory_limit: 268435456\n", "locality {\n", "}\n", "incarnation: 4073418392972710111\n", ", name: \"/device:GPU:0\"\n", "device_type: \"GPU\"\n", "memory_limit: 19146211328\n", "locality {\n", " bus_id: 1\n", " links {\n", " }\n", "}\n", "incarnation: 16025480191014330435\n", "physical_device_desc: \"device: 0, name: Tesla V100-DGXS-32GB, pci bus id: 0000:07:00.0, compute capability: 7.0\"\n", "]\n" ] } ], "source": [ "from tensorflow.python.client import device_lib\n", "print(device_lib.list_local_devices())" ] }, { "cell_type": "markdown", "id": "cbad9661", "metadata": {}, "source": [ "본인이 작성하고 있는 노트북 GPU 환경을 알 수 있는 코드입니다. 저는 ainize workspace에서 Tesla V100 환경에서 노트북을 테스트했기 때문에 Tesla V100-DGXS-32GB이라고 잘 나오네요." ] }, { "cell_type": "code", "execution_count": 3, "id": "4a699d02", "metadata": {}, "outputs": [], "source": [ "#!pip install -U transformers datasets scipy scikit-learn" ] }, { "cell_type": "markdown", "id": "8c957b4c", "metadata": {}, "source": [ "모델 훈련을 위한 transformers와 학습 데이터셋 로드를 위해 datasets 라이브러리를 설치합니다. 그 외 모델 성능 검증을 위해 scipy, scikit-learn 또한 추가로 설치해줍니다.\n", "\n", "\n", "## 문장 분류 모델 학습" ] }, { "cell_type": "code", "execution_count": 4, "id": "401dacc7", "metadata": {}, "outputs": [], "source": [ "import random\n", "import logging\n", "import torch\n", "from IPython.display import display, HTML\n", "import numpy as np\n", "import pandas as pd\n", "import datasets\n", "from datasets import load_dataset, load_metric, ClassLabel, Sequence, list_datasets\n", "from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer\n" ] }, { "cell_type": "markdown", "id": "0ae8dc56", "metadata": {}, "source": [ "노트북을 실행하는데 필요한 라이브러리를 모두 임포트 해줍니다." ] }, { "cell_type": "code", "execution_count": 5, "id": "1d99beee", "metadata": {}, "outputs": [], "source": [ "model_checkpoint = \"klue/roberta-base\"\n", "batch_size = 32\n", "task = \"nli\"" ] }, { "cell_type": "markdown", "id": "048072e8", "metadata": {}, "source": [ "학습에 필요한 정보를 변수로 기록합니다.\n", "\n", "본 노트북애서는 klue-roberta-base 모델을 활용하지만, https://huggingface.co/klue 페이지에서 더 다양한 사전학습 언어 모델을 확인하실 수 있습니다.\n", "\n", "학습 태스크로는 nli를, 배치 사이즈는 32로 지정하겠습니다." ] }, { "cell_type": "code", "execution_count": 6, "id": "5e5c4151", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Reusing dataset kor_nlu (/workspace/.cache/huggingface/datasets/kor_nlu/nli/1.0.0/4facbba77df60b0658056ced2052633e681a50187b9428bd5752ebd59d332ba8)\n" ] } ], "source": [ "datasets = load_dataset(\"kor_nlu\", task)" ] }, { "cell_type": "markdown", "id": "06d9dc6d", "metadata": {}, "source": [ "저는 학습에 사용할 데이터셋을 kakaobrain에서 제공하는 KorNLI 데이터셋을 사용할 것이므로 HuggingFace datasets 라이브러리에 등록된 kor_nlu의 nli 데이터을 다운로드 합니다." ] }, { "cell_type": "code", "execution_count": 7, "id": "01457377", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatasetDict({\n", " train: Dataset({\n", " features: ['premise', 'hypothesis', 'label'],\n", " num_rows: 550146\n", " })\n", " validation: Dataset({\n", " features: ['premise', 'hypothesis', 'label'],\n", " num_rows: 1570\n", " })\n", " test: Dataset({\n", " features: ['premise', 'hypothesis', 'label'],\n", " num_rows: 4954\n", " })\n", "})" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "datasets" ] }, { "cell_type": "markdown", "id": "a8291fbf", "metadata": {}, "source": [ "다운로드 후 얻어진 datasets의 객체를 보면 KorNLI 데이터에는 훈련 데이터, 검증 데이터, 테스트 데이터가 포함되어 있는 것을 확인할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 8, "id": "07659e8c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'premise': '말을 탄 사람이 고장난 비행기 위로 뛰어오른다.',\n", " 'hypothesis': '한 사람이 경쟁을 위해 말을 훈련시키고 있다.',\n", " 'label': 1}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "datasets[\"train\"][0]" ] }, { "cell_type": "markdown", "id": "29d24878", "metadata": {}, "source": [ "각 데이터는 위와 같이 두개의 문장과 두 문장의 추론 관계를 라벨로 가지고 있습니다." ] }, { "cell_type": "code", "execution_count": 9, "id": "33ce978b", "metadata": {}, "outputs": [], "source": [ "def show_random_elements(dataset, num_examples=10):\n", " assert num_examples <= len(dataset), \"Can't pick more elements than there are in the dataset.\"\n", "\n", " picks = []\n", " \n", " for _ in range(num_examples):\n", " pick = random.randint(0, len(dataset)-1)\n", "\n", " # 이미 등록된 예제가 뽑힌 경우, 다시 추출\n", " while pick in picks:\n", " pick = random.randint(0, len(dataset)-1)\n", "\n", " picks.append(pick)\n", "\n", " # 임의로 추출된 인덱스들로 구성된 데이터 프레임 선언\n", " df = pd.DataFrame(dataset[picks])\n", "\n", " for column, typ in dataset.features.items():\n", " # 라벨 클래스를 스트링으로 변환\n", " if isinstance(typ, ClassLabel):\n", " df[column] = df[column].transform(lambda i: typ.names[i])\n", "\n", " display(HTML(df.to_html()))" ] }, { "cell_type": "markdown", "id": "58c8a3aa", "metadata": {}, "source": [ "데이터셋을 전반적으로 살펴보기 위해 시각화 함수를 정의합니다." ] }, { "cell_type": "code", "execution_count": 10, "id": "d4500f78", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | premise | \n", "hypothesis | \n", "label | \n", "
---|---|---|---|
0 | \n", "젊고 늙은 여성들은 큰 잔디밭에서 사회, 정치, 교회 모임이 될 수 있는 곳에 그려져 있다. | \n", "한 무리의 여성이 교회 지하실에서 정치적 회의를 하고 있다. | \n", "contradiction | \n", "
1 | \n", "한 사람이 차 옆 바위가 많은 곳에서 물건을 줍는다. | \n", "사람이 밖에 있다. | \n", "entailment | \n", "
2 | \n", "하얀 셔츠를 입은 남자가 야외 테이블에서 여자 맞은편에 앉아 있다. | \n", "커다란 인간이 앉아 있었다. | \n", "neutral | \n", "
3 | \n", "오렌지색 조끼를 입은 두 명의 직원이 서류 작업을 하고 있다. | \n", "사람들이 자고 있다. | \n", "contradiction | \n", "
4 | \n", "빨간 저지를 입은 축구선수가 공을 던지려고 하는 하얀 저지를 입은 축구선수와 싸우고 있다. | \n", "수녀가 카페에서 커피를 주문한다. | \n", "contradiction | \n", "
5 | \n", "노란 원피스와 분홍색 샌들을 신은 아이가 커다란 물에서 걸어간다. | \n", "한 아이가 그녀의 개를 호수에서 쫓아낸다. | \n", "neutral | \n", "
6 | \n", "초라한 드레스를 입은 여자. | \n", "그 드레스는 약간 쌀쌀하다. | \n", "neutral | \n", "
7 | \n", "두 마리의 말이 한 여자를 수레에 태우고 있다. | \n", "여자가 마차를 타고 말을 탄다. | \n", "entailment | \n", "
8 | \n", "낙서로 뒤덮인 벽돌담을 지나가는 남자와 여자. | \n", "두 사람이 벽에 걸린 낙서를 지나간다. | \n", "entailment | \n", "
9 | \n", "인도 여성들이 전통무용에 손을 잡고 있다. | \n", "인도 여성들은 결혼식에서 전통 춤을 춘다. | \n", "neutral | \n", "