{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "6PhvxVaphc2J" }, "source": [ "# `pororo`를 사용한 한글 자연어 처리" ] }, { "cell_type": "markdown", "metadata": { "id": "xdYuVrhphc2M" }, "source": [ "*아래 링크를 통해 이 노트북을 주피터 노트북 뷰어(nbviewer.jupyter.org)로 보거나 구글 코랩(colab.research.google.com)에서 실행할 수 있습니다.*\n", "\n", "\n", " \n", " \n", "
\n", " 주피터 노트북 뷰어로 보기\n", " \n", " 구글 코랩(Colab)에서 실행하기\n", "
" ] }, { "cell_type": "markdown", "metadata": { "id": "qQBfQgtNhc2M" }, "source": [ "2021년 초에 카카오브레인([https://www.kakaobrain.com/](https://www.kakaobrain.com/))에서 다양한 한글 자연어 처리 작업을 위한 `pororo`('뽀로로'라고 읽습니다)([https://github.com/kakaobrain/pororo](https://github.com/kakaobrain/pororo)) 파이썬 라이브러리를 릴리스했습니다. `pororo` 라이브러리는 BERT, Transformer 등 파이토치로 구현된 최신 NLP 모델을 사용해 30여 가지의 자연어 처리 작업을 수행합니다. 여기에서는 이 중에 대표적인 몇 가지 작업에 대해서 알아 보겠습니다. `pororo` 라이브러리가 수행할 수 있는 전체 작업 목록은 온라인 문서([https://kakaobrain.github.io/pororo/index.html](https://kakaobrain.github.io/pororo/index.html))를 참고하세요.\n", "\n", "`pororo`라이브러리는 `pip` 명령으로 간단히 설치할 수 있습니다. 현재는 파이썬 3.6 버전 이상과 파이토치 1.6 버전(CUDA 10.1)을 지원합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "RbepwTNXhc2N" }, "outputs": [], "source": [ "# 코랩을 사용하는 경우 다음 주석을 해제하고 pororo를 먼저 설치하세요.\n", "# !pip install pororo" ] }, { "cell_type": "markdown", "metadata": { "id": "YgRvyl7phc2O" }, "source": [ "`pororo`를 사용하는 방법은 `Pororo` 클래스에 원하는 작업을 지정하여 작업에 맞는 클래스 객체를 얻는 것입니다. 전체 작업 목록은 온라인 문서에 있으며 다음과 같이 `available_tasks()` 메서드를 호출하여 얻을 수 있습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "hgNdwWiVhc2O", "outputId": "0d895b43-f2bf-4e84-892b-522a368dea62" }, "outputs": [ { "data": { "text/plain": [ "\"Available tasks are ['mrc', 'rc', 'qa', 'question_answering', 'machine_reading_comprehension', 'reading_comprehension', 'sentiment', 'sentiment_analysis', 'nli', 'natural_language_inference', 'inference', 'fill', 'fill_in_blank', 'fib', 'para', 'pi', 'cse', 'contextual_subword_embedding', 'similarity', 'sts', 'semantic_textual_similarity', 'sentence_similarity', 'sentvec', 'sentence_embedding', 'sentence_vector', 'se', 'inflection', 'morphological_inflection', 'g2p', 'grapheme_to_phoneme', 'grapheme_to_phoneme_conversion', 'w2v', 'wordvec', 'word2vec', 'word_vector', 'word_embedding', 'tokenize', 'tokenise', 'tokenization', 'tokenisation', 'tok', 'segmentation', 'seg', 'mt', 'machine_translation', 'translation', 'pos', 'tag', 'pos_tagging', 'tagging', 'const', 'constituency', 'constituency_parsing', 'cp', 'pg', 'collocation', 'collocate', 'col', 'word_translation', 'wt', 'summarization', 'summarisation', 'text_summarization', 'text_summarisation', 'summary', 'gec', 'review', 'review_scoring', 'lemmatization', 'lemmatisation', 'lemma', 'ner', 'named_entity_recognition', 'entity_recognition', 'zero-topic', 'dp', 'dep_parse', 'caption', 'captioning', 'asr', 'speech_recognition', 'st', 'speech_translation', 'tts', 'text_to_speech', 'speech_synthesis', 'ocr', 'srl', 'semantic_role_labeling', 'p2g', 'aes', 'essay', 'qg', 'question_generation', 'age_suitability', 'wsd']\"" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from pororo import Pororo\n", "\n", "Pororo.available_tasks()" ] }, { "cell_type": "markdown", "metadata": { "id": "No6AGxg5hc2P" }, "source": [ "## 광학 문자 인식" ] }, { "cell_type": "markdown", "metadata": { "id": "ozBK2X68hc2Q" }, "source": [ "먼저 이미지에서 문자를 읽는 광학 문자 인식(Optical Character Recognition) 작업을 수행해 보겠습니다. 광학 문자 인식 작업을 수행하려면 `Pororo` 클래스에 `task='ocr'` 매개변수를 지정하여 객체를 만듭니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Xoip64ruhc2Q", "scrolled": true }, "outputs": [], "source": [ "ocr = Pororo(task='ocr')" ] }, { "cell_type": "markdown", "metadata": { "id": "iqa5xguzhc2R" }, "source": [ "`ocr` 객체를 사용하는 방법은 간단합니다. 문자 인식을 하려는 이미지를 매개변수로 전달하면 됩니다. 다음과 같은 책 표지 이미지를 사용해 보죠." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "tcmbv67Dhc2R" }, "outputs": [], "source": [ "# 코랩을 사용하는 경우 다음 주석을 해제하고 이미지를 먼저 다운로드하세요.\n", "# !wget https://github.com/rickiepark/nlp-with-pytorch/raw/main/ocr-test.png" ] }, { "cell_type": "markdown", "metadata": { "id": "3jxl_9i6hc2R" }, "source": [ "![](https://github.com/rickiepark/nlp-with-pytorch/raw/main/ocr-test.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "sz-Q_nmShc2R", "outputId": "d8ae1cc2-0bb3-41db-dd34-2e53bc8b3a11" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/work/.local/lib/python3.8/site-packages/torch/nn/functional.py:3384: UserWarning: Default grid_sample and affine_grid behavior has changed to align_corners=False since 1.3.0. Please specify align_corners=True if the old behavior is desired. See the documentation of grid_sample for details.\n", " warnings.warn(\"Default grid_sample and affine_grid behavior has changed \"\n" ] }, { "data": { "text/plain": [ "['Machine Leaming with Python Cookbook 파이썬을 활용한 머신러닝 쿡북',\n", " '크리스 알본 지음 빅해선 옮김',\n", " \"Introduction to Machine Learning with Pythan 안드레아스 뮐러. 세라 가이도 지음 파이썬 라이브러리를 활용한 머신러닝 번역개정판 '해선 옮김\",\n", " 'Hands-On Machine Learning with Scikit-Learn, Keras & TensorFlow',\n", " \"핸즈온 머신러닝 '] 오렐리앙 제롱 지음 박해선 옮김\",\n", " 'GANS 텐서플로 2.x와 케라스로 구축하는 야쿠프 란그르, 블라디미르 보크 지음 GAN 인 액션 생성적 적대 신경망 박해선 옮김 INAGTION',\n", " '데이비드 포스터 지음 Generative 미술관에 GAN 딥러닝 실전 프로젝트 Deep Learning 박해선 옮김']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ocr('ocr-test.png')" ] }, { "cell_type": "markdown", "metadata": { "id": "vTIQvWaThc2S" }, "source": [ "<핸즈온 머신러닝>에서 세로로 쓰여진 '2판'은 인식을 못했고 과 <미술관에 GAN 딥러닝>은 행을 조금 혼동하고 있지만 전반적으로 높은 인식율을 보여주고 있습니다.\n", "\n", "광학 문자 인식 작업에 지원하는 언어는 영어와 한국어입니다. 지원하는 언어 목록을 보려면 `pororo` 패키지의 온라인 문서를 참고하세요. 현재는 `Pororo` 클래스에서 가능한 언어를 직접 확인할 수는 없습니다. 다만 다음처럼 `SUPPORTED_TASKS` 딕셔너리에 매핑된 광학 문자 인식 클래스의 `get_available_langs()` 정적 메서드를 호출하여 확인할 수 있습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CgsQ_UNJhc2S", "outputId": "e97076e7-2c09-4bef-bd41-0eeab9fbe26c", "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['en', 'ko']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from pororo.pororo import SUPPORTED_TASKS\n", "\n", "SUPPORTED_TASKS['ocr'].get_available_langs()" ] }, { "cell_type": "markdown", "metadata": { "id": "Uh9cw8jphc2S" }, "source": [ "로컬에 있는 파일 뿐만 아니라 URL을 전달할 수도 있습니다. 다음과 같이 영어로 쓰여진 표지판([https://bit.ly/london-sign](https://bit.ly/london-sign), Goldflakes, CC BY-SA 4.0)을 인식해 보죠." ] }, { "cell_type": "markdown", "metadata": { "id": "r1HOrDZohc2S" }, "source": [ "![](https://bit.ly/london-sign)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "fa8Jw12mhc2T", "outputId": "8b01a16e-1129-4ac0-99c0-c807678660c3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Progress: |██████████████████████████████████████████████████| 101.0% Complete\r" ] }, { "data": { "text/plain": [ "{'description': ['Central London A4 (West End) alternative route for goods vehicles',\n", " '37n',\n", " 'pm 6 am',\n", " 'C. London (Westminster A3220 (A3212)'],\n", " 'bounding_poly': [{'description': 'Central London A4 (West End) alternative route for goods vehicles',\n", " 'vertices': [{'x': 98, 'y': 68},\n", " {'x': 330, 'y': 68},\n", " {'x': 330, 'y': 182},\n", " {'x': 98, 'y': 182}]},\n", " {'description': '37n',\n", " 'vertices': [{'x': 174, 'y': 254},\n", " {'x': 232, 'y': 254},\n", " {'x': 232, 'y': 280},\n", " {'x': 174, 'y': 280}]},\n", " {'description': 'pm 6 am',\n", " 'vertices': [{'x': 160, 'y': 328},\n", " {'x': 254, 'y': 328},\n", " {'x': 254, 'y': 356},\n", " {'x': 160, 'y': 356}]},\n", " {'description': 'C. London (Westminster A3220 (A3212)',\n", " 'vertices': [{'x': 132, 'y': 380},\n", " {'x': 333, 'y': 380},\n", " {'x': 333, 'y': 469},\n", " {'x': 132, 'y': 469}]}]}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ocr('https://bit.ly/london-sign', detail=True)" ] }, { "cell_type": "markdown", "metadata": { "id": "SUxt5CHphc2T" }, "source": [ "결과에서 알 수 있듯이 이미지 구역에 따라 인식한 글씨를 나누어 리스트로 반환하고 있습니다. 또한 `detail=True`로 지정하면 인식된 글자 구역의 왼쪽 위에서 시계 방향으로 4개의 사각형 모서리 좌표를 반환합니다. `pororo`의 광학 인식 문자에 사용되는 OCR 모델은 내부 데이터와 AI hub의 한국어 글자체 이미지 AI 데이터([https://www.aihub.or.kr/aidata/133](https://www.aihub.or.kr/aidata/133))을 사용하여 훈련되었습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "vLbjbkp2hc2T" }, "source": [ "## 이미지 캡셔닝" ] }, { "cell_type": "markdown", "metadata": { "id": "NJRwJKVShc2T" }, "source": [ "이미지 캡셔닝(image captioning)은 이미지를 설명하는 텍스트를 만드는 작업입니다. `pororo`의 이미지 캡션은 한국어, 영어, 중국어, 일본어를 지원합니다. 가능한 언어 목록을 확인해 보죠. 이미지 캡셔닝 작업은 `'caption'`으로 지정합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UKArRyEhhc2T", "outputId": "d8883f94-ea65-4fdd-fec0-d724047a6e22" }, "outputs": [ { "data": { "text/plain": [ "['en', 'ko', 'zh', 'ja']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "SUPPORTED_TASKS['caption'].get_available_langs()" ] }, { "cell_type": "markdown", "metadata": { "id": "213IleW9hc2U" }, "source": [ "광학 문자 인식과 마찬가지로 `task` 매개변수에 `'caption'`으로 지정하고 `lang='ko'`으로 지정하여 한글 캡션을 위한 객체를 만들어 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nbq5H6Mghc2U", "outputId": "efc22ed8-b28d-4c4a-ed3c-d38470b26924", "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using cache found in /home/work/.cache/torch/hub/facebookresearch_detr_master\n" ] } ], "source": [ "caption = Pororo(task='caption', lang='ko')" ] }, { "cell_type": "markdown", "metadata": { "id": "m69sTz1thc2U" }, "source": [ "`Pororo` 클래스는 새로운 객체를 만들 때마다 사용할 모델을 다운로드하여 로드합니다. 다운로드된 데이터는 리눅스일 경우 `~/.pororo` 아래 저장되고 윈도우의 경우 `C:\\\\pororo` 아래 저장하여 나중에 재사용합니다.\n", "\n", "다음과 같은 이미지([http://bit.ly/ny-timesquare](http://bit.ly/ny-timesquare), Terabass, CC BY-SA 3.0)의 캡션을 만들어 보겠습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "i5O-NPVphc2U" }, "source": [ "![](http://bit.ly/ny-timesquare)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wxOMLq2Shc2U", "outputId": "c7d212d5-e245-454d-eaa8-a7b235cb246a", "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "'높은 건물들이 가득 차 있는 도시 거리'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "caption('http://bit.ly/ny-timesquare')" ] }, { "cell_type": "markdown", "metadata": { "id": "fRCa3OsAhc2V" }, "source": [ "이번에는 영어로 캡션을 만들어 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "8L1ONfkDhc2V", "outputId": "36faabba-c19a-4eb5-aef0-2d703f5c9f4c" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using cache found in /home/work/.cache/torch/hub/facebookresearch_detr_master\n" ] }, { "data": { "text/plain": [ "'A city street filled with lots of tall buildings.'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "caption = Pororo(task='caption', lang='en')\n", "caption('http://bit.ly/ny-timesquare')" ] }, { "cell_type": "markdown", "metadata": { "id": "I3imdRAdhc2V" }, "source": [ "각 작업이 사용하는 모델은 `Pororo` 클래스의 `available_models()` 정적 메서드에서 얻을 수 있습니다. 이 메서드를 호출할 때 `task` 매개변수에 작업 이름을 지정합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "X_SoXexjhc2V", "outputId": "17ce3e6e-51b0-4401-b2ef-e36477bc9ece" }, "outputs": [ { "data": { "text/plain": [ "'Available models for caption are ([lang]: en, [model]: transformer.base.en.caption), ([lang]: ko, [model]: transformer.base.en.caption), ([lang]: zh, [model]: transformer.base.en.caption), ([lang]: ja, [model]: transformer.base.en.caption)'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Pororo.available_models(task='caption')" ] }, { "cell_type": "markdown", "metadata": { "id": "CYe6ynp1hc2V" }, "source": [ "또는 앞에서와 같이 `SUPPORTED_TASKS` 딕셔너리 객체를 사용해 얻은 클래스의 `get_available_models()` 메서드를 호출할 수도 있습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "gUian0Duhc2V", "outputId": "c06547a0-5304-48a3-aad2-71c5dc0a6fda" }, "outputs": [ { "data": { "text/plain": [ "{'en': ['transformer.base.en.caption'],\n", " 'ko': ['transformer.base.en.caption'],\n", " 'zh': ['transformer.base.en.caption'],\n", " 'ja': ['transformer.base.en.caption']}" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "SUPPORTED_TASKS['caption'].get_available_models()" ] }, { "cell_type": "markdown", "metadata": { "id": "-rrt6exahc2W" }, "source": [ "사용하는 모델 목록에서 볼 수 있듯이 이미지 캡셔닝은 트랜스포머 기반의 영어 모델만 사용합니다. 그외 언어에 대해서는 사실 다음 절에서 설명할 기계 번역 모델을 통해 번역한 것입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "cCZlVuwUhc2W" }, "source": [ "## 기계 번역" ] }, { "cell_type": "markdown", "metadata": { "id": "kTtMlpidhc2W" }, "source": [ "기계 번역 작업은 페이스북에서 만든 fairseq의 TransformerModel(https://fairseq.readthedocs.io/en/latest/models.html#module-fairseq.models.transformer) 모델을 사용합니다. 훈련은 내부 데이터를 사용했고 테스트 데이터는 Multilingual TED Talk(http://www.cs.jhu.edu/~kevinduh/a/multitarget-tedtalks/) 데이터를 사용했습니다.\n", "\n", "번역 작업은 `'translation'`이며 `lang` 매개변수는 `multi`로 지정하며 한국어, 영어, 일본어, 중국어를 번역할 수 있습니다. 또한 `model` 매개변수에 사용할 모델을 지정할 수 있습니다. 기본적으로 인코더와 디코더가 각각 6개의 층으로 이루어진 `'transformer.large.multi.mtpg'`을 사용합니다. 기본 모델을 사용해 기계 번역 객체를 만들어 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "zaaQS80Ihc2W", "scrolled": true }, "outputs": [], "source": [ "mt = Pororo(task='translation', lang='multi')" ] }, { "cell_type": "markdown", "metadata": { "id": "QSC0I9-Mhc2W" }, "source": [ "한국어 샘플 텍스트를 영어로 번역해 보겠습니다. 원본 텍스트의 언어는 `src`에 지정하고 번역하려는 타깃 언어는 `tgt`에 지정합니다. 지정할 수 있는 옵션은 앞에서 언급한 4개의 언어인 `'ko'`, `'en'`, `'ja'`, `'zh'`입니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JxlYi4Qzhc2W" }, "outputs": [], "source": [ "text1 = '퍼서비어런스(Perseverance)는 화성 탐사차로 2020년 7월 30일 발사하여 2021년 2월 18일 화성에 착륙하였다. 화성의 생명체 거주 여부, 화성의 고대 환경 조사, 화성 지표의 역사 등을 밝히는 것이 이 탐사선의 목표다. 더불어 중요한 목표는 미래의 인류가 화성을 유인 탐사할 때 위험한 것이 없는지 탐색하고, 대기의 조성을 알려주어 미래의 기지를 건설하는 데 도움을 주는 것이다. 또 인간이 어떤 조건으로 착륙해야 되는지 등을 탐색한다. 예산은 원래 15억 달러를 배정했는데, 지금은 더 늘어나서 25억 달러다. 특이사항으로는 인사이트가 마스 큐브 원과 화성에 함께 갔던 것과 비슷하게 인제뉴어티와 함께 발사되었다. 또한 큐리오시티의 디자인을 많이 재사용했다. 따라서 새로운 기술보다는 이전 로버들의 좋은 점을 합친 것이라고 보면 된다. 참고로, 마스 2020(Mars 2020)은 퍼서비어런스와 인제뉴어티 드론 헬리콥터를 포함한, NASA의 화성 지표면 로봇 탐사 계획의 명칭이다. 최초로 화성의 바람소리를 녹음했다.'" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Rh2KCwrGhc2X", "outputId": "d22e6762-f273-453b-f64b-28f675348864" }, "outputs": [ { "data": { "text/plain": [ "\"Perseverance was launched on July 30, 2020 by a Mars exploration vehicle and landed on Mars on February 18, 2021. The probe's goal is to reveal whether Mars lives, the ancient environmental survey of Mars, and the history of Mars' indicators. In addition, the important goal is to explore whether there is any danger when future humans attract Mars, and to inform the creation of the atmosphere to help build a future base. We also explore what conditions humans should land on. The budget originally allocated $1.5 billion, which is $2.5 billion now. In particular, the Internet was launched with Inje New York, similar to the fact that Insight went to Mars Cube One and Mars. It also reused many of the design of Curio City. Therefore, it can be seen as a combination of the good points of the previous Roberts rather than the new technology. By reference, Mars 2020 is the name of the NASA's Mars surface robot exploration plan, including the Parseverance and the International New York drone helicopter. For the first time, I recorded the wind of Mars.\"" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mt(text1, src='ko', tgt='en')" ] }, { "cell_type": "markdown", "metadata": { "id": "yHByF2Ashc2X" }, "source": [ "몇몇 번역 오류가 있지만 대체적으로 번역 결과는 좋습니다.\n", "\n", "기본 모델 외에 약간의 성능을 희생하면서 2배 정도 빠른 속도를 내는 `'transformer.large.multi.fast.mtpg'` 모델을 지정할 수 있습니다. 이 모델은 12개의 인코더 층과 1개의 디코더 층으로 이루어집니다. 이 모델을 사용해 앞에서와 동일한 텍스트를 번역해 보죠." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "38l34Ecehc2X" }, "outputs": [], "source": [ "mt_fast = Pororo(task='translation', lang='multi', model='transformer.large.multi.fast.mtpg')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "RKG2-TSkhc2X", "outputId": "36c29b2d-8986-449e-a47e-62f7b0a6ba3a", "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "\"Perseverance was launched on July 30, 2020 and landed on Mars on February 18, 2021. The probe's goal is to reveal whether Mars lives in life, the ancient environmental survey of Mars, and the history of Mars indicators. In addition, an important goal is to explore whether there is anything dangerous when investigating Mars, and inform the creation of the atmosphere to help build a future base. It also explore what conditions humans have to land. The budget was originally allocated $1.5 billion, which is now increasing further and $2.5 billion. In particular, Insight was launched with Inje New Art, similar to the fact that Insight went with Mas Cube One and Hwaseong. Also, the design of Curio City has been reused. Therefore, you can see that it combines the good points of previous Robert rather than new technologies. For reference, Mas 2020 (Mars 2020) is the name of NASA's Mars surface robot exploration plan, including Per-subarance and Inje Newarty drone helicopter. For the first time, I recorded the wind of Mars.\"" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mt_fast(text1, src='ko', tgt='en')" ] }, { "cell_type": "markdown", "metadata": { "id": "3lfz9S-5hc2Y" }, "source": [ "이제 조금 더 재미있는 작업인 텍스트 요약을 다루어 보겠습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "MnMu5457hc2Y" }, "source": [ "## 텍스트 요약" ] }, { "cell_type": "markdown", "metadata": { "id": "2uK56f6mhc2Y" }, "source": [ "텍스트 요약(text summarization)은 비교적 긴 텍스트를 짧은 문장 몇개로 압축하여 표현하는 작업입니다. `pororo`는 텍스트 요약을 위해 3개의 모델을 제공합니다.\n", "\n", "먼저 `abstractive` 모델은 하나의 완전한 문장으로 텍스트 내용을 요약합니다. 이 모델은 SKT에서 개발한 KoBART(https://github.com/SKT-AI/KoBART) 모델을 사용합니다. 학습에 사용한 데이터는 데이콘(DACON)의 문서 추출요약 경진 대회 데이터(https://dacon.io/competitions/official/235671/data/)와 AI 허브에서 공개한 AI 학습용 데이터(https://www.aihub.or.kr/node/9176)입니다.\n", "\n", "`task='summary'`로 지정하여 텍스트 요약 작업임을 알리고 `lang='ko'`로 지정합니다. 현재 텍스트 요약 작업은 한글만 지원합니다. 먼저 `abstractive` 모델을 사용해 보죠. " ] }, { "cell_type": "markdown", "metadata": { "id": "PkVZHw9-h_KN" }, "source": [ "**경고**: 최신 `transformers` 패키지에서 나눗셈 에러가 발생할 수 있습니다. 이런 경우 다음 명령으로 `transformers` 4.7.0 버전을 설치해 주세요.\n", "\n", "`!pip install -U transformers==4.7.0`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PUsN-Nxyhc2Y" }, "outputs": [], "source": [ "abs_summ = Pororo(task='summary', lang='ko', model='abstractive')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Xq5GOBD6hc2Y", "outputId": "cdc33ab1-ae45-409b-b49a-29c13592213d" }, "outputs": [ { "data": { "text/plain": [ "'미래의서비어런스는 화성의 생명체 거주 여부, 화성의 고대 환경 조사, 화성 지표의 역사 등을 밝히는 것이 목표이며 미래의 인류가 화성을 유인 탐사할 때 위험한 것이 없는지 탐색하고, 대기의 조성을 알려주어 미래의 기지를 건설하는 데 도움을 주는 역할을 수행한다.'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs_summ(text1)" ] }, { "cell_type": "markdown", "metadata": { "id": "TOV-s58Uhc2Y" }, "source": [ "꽤 잘 요약된 것 같습니다. 다음에는 `bullet` 모델을 사용해 보겠습니다. 이 모델은 짧은 몇 개의 문장으로 텍스트를 요약합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HUGkBGlbhc2Y" }, "outputs": [], "source": [ "bul_summ = Pororo(task='summary', lang='ko', model='bullet')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nnn0S8IZhc2Y", "outputId": "3d3f9e92-2e78-4131-c56b-9d2888a4e654" }, "outputs": [ { "data": { "text/plain": [ "['NASA 화성 지표면 로봇 탐사 계획', ' 퍼서비어런스, 인제뉴어티 드론 헬리콥터 포함']" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bul_summ(text1)" ] }, { "cell_type": "markdown", "metadata": { "id": "ftaAQkGahc2Z" }, "source": [ "텍스트 요약 작업은 여러 개의 텍스트를 파이썬 리스트로 만들어 전달하면 한 번에 여러 텍스트를 요약할 수 있습니다. 예를 위해 두 번째 텍스트 샘플을 만들어 `text1`과 함께 전달해 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "oukPFmSlhc2Z" }, "outputs": [], "source": [ "text2 = \"알로사우루스(라틴어: Allosaurus)는 후기 쥐라기(1억 5600만 년 전 ~ 1억 4500만 년 전)를 대표하는 큰 육식공룡이다. 알로사우루스라는 학명의 어원은 고대 그리스어 (그리스어: αλλοςσαυρος)인데, 이 말은 '특이한 도마뱀'이라는 뜻으로, 한자표기 이특룡(異特龍)은 여기서 비롯되었다. 미국의 고생물학자 오스니얼 찰스 마시가 알로사우루스속 화석을 처음으로 기재했다. 수각류 공룡 중 비교적 초기에 알려진 공룡 중 하나로, 그동안 여러 영화나 다큐멘터리에도 많이 등장했다. 알로사우루스는 짧은 목에 큰 머리, 긴 꼬리와 짧은 앞다리를 가진 전형적으로 거대한 수각류 공룡이다. 생존 당시에는 대형 포식자로서 먹이사슬의 최고점에 있었다. 보통 아성체나 소형 용각류, 조각류, 검룡류와 같은 대형 초식공룡을 사냥했을 것으로 추정된다. 아성체나 소형 용각류 등을 사냥할 때 무리를 지어 조직적으로 사냥했다는 추정이 있지만, 이들이 사회적이라는 증거는 많지 않으며 심지어 자신들끼리 싸움을 했을 수도 있다. 매복하다가 입을 크게 벌리고 윗턱을 손도끼 내리치듯이 가격해 큰 사냥감을 잡았을 것으로 생각된다.\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HspVXPr3hc2Z", "outputId": "842dad7a-bca9-4629-d1b2-bfc0166b4739" }, "outputs": [ { "data": { "text/plain": [ "[['NASA 화성 지표면 로봇 탐사 계획', ' 퍼서비어런스, 인제뉴어티 드론 헬리콥터 포함'],\n", " ['짧은 목에 큰 머리, 긴 꼬리, 짧은 앞다리', ' 후기 쥐라기 대표하는 큰 육식공룡']]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bul_summ([text1, text2])" ] }, { "cell_type": "markdown", "metadata": { "id": "E151edOzhc2Z" }, "source": [ "이번에는 텍스트에서 가중 중요한 3개의 문장을 추출하는 `extractive` 모델을 사용해 보겠습니다. 이 모델은 페이스북의 RoBERTa(https://ai.facebook.com/blog/roberta-an-optimized-method-for-pretraining-self-supervised-nlp-systems/) 모델을 위에서 언급한 말뭉치에서 훈련시켜 사용합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "-0opWW36hc2Z", "outputId": "e9b5972d-babc-41fc-939c-1a14b0fbb9db" }, "outputs": [ { "data": { "text/plain": [ "'퍼서비어런스(Perseverance)는 화성 탐사차로 2020년 7월 30일 발사하여 2021년 2월 18일 화성에 착륙하였다. 화성의 생명체 거주 여부, 화성의 고대 환경 조사, 화성 지표의 역사 등을 밝히는 것이 이 탐사선의 목표다. 참고로, 마스 2020(Mars 2020)은 퍼서비어런스와 인제뉴어티 드론 헬리콥터를 포함한, NASA의 화성 지표면 로봇 탐사 계획의 명칭이다.'" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ext_summ = Pororo(task='summary', lang='ko', model='extractive')\n", "ext_summ(text1)" ] }, { "cell_type": "markdown", "metadata": { "id": "NznyTL7ohc2Z" }, "source": [ "마찬가지로 여러 개의 문장을 리스트로 묶어서 전달할 수 있으며 `return_list=True`로 지정하면 추출한 3개의 문장을 리스트로 만들어 반환합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "jJxMxhvthc2Z", "outputId": "63d4c45e-eebc-4958-8d06-f12f287f1dff" }, "outputs": [ { "data": { "text/plain": [ "[['퍼서비어런스(Perseverance)는 화성 탐사차로 2020년 7월 30일 발사하여 2021년 2월 18일 화성에 착륙하였다.',\n", " '화성의 생명체 거주 여부, 화성의 고대 환경 조사, 화성 지표의 역사 등을 밝히는 것이 이 탐사선의 목표다.',\n", " '참고로, 마스 2020(Mars 2020)은 퍼서비어런스와 인제뉴어티 드론 헬리콥터를 포함한, NASA의 화성 지표면 로봇 탐사 계획의 명칭이다.'],\n", " ['알로사우루스(라틴어: Allosaurus)는 후기 쥐라기(1억 5600만 년 전 ~ 1억 4500만 년 전)를 대표하는 큰 육식공룡이다.',\n", " '알로사우루스는 짧은 목에 큰 머리, 긴 꼬리와 짧은 앞다리를 가진 전형적으로 거대한 수각류 공룡이다.',\n", " '생존 당시에는 대형 포식자로서 먹이사슬의 최고점에 있었다.']]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ext_summ([text1, text2], return_list=True)" ] }, { "cell_type": "markdown", "metadata": { "id": "FqTObH77hc2a" }, "source": [ "## 감성 분석" ] }, { "cell_type": "markdown", "metadata": { "id": "F4o_c_hLhc2a" }, "source": [ "감성 분석(sentiment analysis)는 텍스트를 긍정과 부정으로 분류하는 작업입니다. `pororo`는 페이스북의 RoBERTa 모델을 네이버 쇼핑 리뷰 데이터셋([https://github.com/bab2min/corpus/tree/master/sentiment](https://github.com/bab2min/corpus/tree/master/sentiment))과 네이버 영화 리뷰 데이터셋([https://github.com/e9t/nsmc](https://github.com/e9t/nsmc))에서 훈련한 모델을 제공합니다. 또한 일본어를 위한 모델도 제공합니다.\n", "\n", "먼저 네이버 쇼핑 리뷰 데이터셋에서 훈련한 모델(`model='brainbert.base.ko.shopping'`)을 사용해 보죠. 이 모델은 네이버 쇼핑 리뷰 데이터셋에서 약 95%의 정확도를 달성했습니다. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OiC10sqZhc2a" }, "outputs": [], "source": [ "sa_shop = Pororo(task='sentiment', model='brainbert.base.ko.shopping', lang='ko')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "IPAAVK2fhc2a", "outputId": "7d240a78-2f8b-4dd1-a5a3-4a69a0ac8ae9" }, "outputs": [ { "data": { "text/plain": [ "'Positive'" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_shop('정말 혼자 공부하기 너무 좋은 머신러닝 독학 책')" ] }, { "cell_type": "markdown", "metadata": { "id": "mR3vNXX2hc2a" }, "source": [ "일반적인 텍스트의 감성은 쉽게 분류합니다. 하지만 비유적인 표현은 쉽게 감지하기 어렵습니다. 아래 텍스트는 달팽이에 비유하여 느린 배송 속도를 비꼬는 말이지만 긍정적으로 분류했습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "6YueUHSrhc2a", "outputId": "a416512c-8470-4d73-cc77-d55b1dec301a" }, "outputs": [ { "data": { "text/plain": [ "'Positive'" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_shop('달팽이 같은 놀라운 배송 속도')" ] }, { "cell_type": "markdown", "metadata": { "id": "Ko0RN8ruhc2a" }, "source": [ "쇼핑 리뷰 데이터를 사용해 훈련했기 때문에 비교적 쇼핑과 관련된 텍스트에 담긴 감정은 잘 잡아내지만 영화에 대한 것은 그렇지 못합니다. 다음 예를 살펴 보죠." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "cG5W6FQVhc2a", "outputId": "c4b4abae-a8a8-4eeb-8c21-9b976df89a6d" }, "outputs": [ { "data": { "text/plain": [ "'Positive'" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_shop('택배 속도 놀랍군')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "eX8wHX5Fhc2a", "outputId": "573a42b1-1a0d-43c6-ac8c-1badf668c6df" }, "outputs": [ { "data": { "text/plain": [ "'Negative'" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_shop('반전을 거듭하는데 와..')" ] }, { "cell_type": "markdown", "metadata": { "id": "4WVZPkxihc2b" }, "source": [ "이번에는 네이터 영화 리뷰 데이터셋에서 훈련한 모델을 사용해 보겠습니다. `model='brainbert.base.ko.nsmc'`로 지정합니다. 이 모델은 네이버 영화 리뷰 데이터셋에서 약 90%의 정확도를 냈습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JuhSwwszhc2b" }, "outputs": [], "source": [ "sa_movie = Pororo(task='sentiment', model='brainbert.base.ko.nsmc', lang='ko')" ] }, { "cell_type": "markdown", "metadata": { "id": "LYZ6IkEChc2b" }, "source": [ "앞에서와 같이 동일한 예를 적용해 보죠. 여기에서는 반대로 영화와 관련된 감정은 잘 감지하지만 택배에 대해서는 그렇지 못합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nq7OiqNwhc2d", "outputId": "b2114837-722a-4a2f-b107-0dbab6cf4876" }, "outputs": [ { "data": { "text/plain": [ "'Negative'" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_movie('택배 속도 놀랍군')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UY7UGUAshc2d", "outputId": "14397350-2f88-45cc-8284-567689cbfe5d" }, "outputs": [ { "data": { "text/plain": [ "'Positive'" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa_movie('반전을 거듭하는데 와..')" ] }, { "cell_type": "markdown", "metadata": { "id": "0v3dpW3chc2d" }, "source": [ "## 자연어 추론" ] }, { "cell_type": "markdown", "metadata": { "id": "cfMdh72Ohc2d" }, "source": [ "자연어 추론(natural language inference, NLI)는 두 문장 사이의 관계가 함의(entailment), 모순(contradiction), 중립(neutral)인지 추론합니다. `pororo`에서 자연어 추론 작업은 `'nli'`로 지정하며 RoBERTa 구조를 사용해 한국어, 영어, 일본어, 중국어 데이터셋에서 훈련한 모델을 제공합니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "r9y4gG2Rhc2d", "outputId": "ef8712fc-7930-48f5-9bc7-277aed324d6b" }, "outputs": [ { "data": { "text/plain": [ "['en', 'ko', 'ja', 'zh']" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "SUPPORTED_TASKS['nli'].get_available_langs()" ] }, { "cell_type": "markdown", "metadata": { "id": "Mrbs8koBhc2d" }, "source": [ "`lang='ko'`로 지정하여 간단한 한국어 문장을 추론해 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "LBCv78JIhc2e" }, "outputs": [], "source": [ "nli = Pororo(task='nli', lang='ko')" ] }, { "cell_type": "markdown", "metadata": { "id": "H2q-gYfHhc2e" }, "source": [ "아래 3개의 예에서 처음 2개는 함의, 모순 관계를 잘 감지했습니다. 하지만 마지막 3번째 예는 함의가 아니라 중립으로 출력된 것을 알 수 있습니다. 이 모델은 두 문장의 인과 관계를 감지하기 쉽지 않아 보입니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "oslAa_jkhc2e", "outputId": "d81be990-90d8-47b8-8676-ca079de5cfc2" }, "outputs": [ { "data": { "text/plain": [ "'Entailment'" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nli('비가 온다', '날씨가 우중충하다')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "l-MUKY5chc2e", "outputId": "6a06da82-bdd8-4ac3-ad04-a8f0c1744767" }, "outputs": [ { "data": { "text/plain": [ "'Contradiction'" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nli('비가 온다', '구름 사이로 햇살이 비친다')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "lEc0PA0jhc2e", "outputId": "a02c5a38-0dde-4265-df40-b76ca50dca75" }, "outputs": [ { "data": { "text/plain": [ "'Neutral'" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nli('비가 온다', '옷이 비에 젖다')" ] }, { "cell_type": "markdown", "metadata": { "id": "JP2Kik-rhc2e" }, "source": [ "다음 절에서 자연어 추론과 관계가 깊은 제로샷 토픽 분류에 대해 알아 보겠습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "RuRVAFk0hc2e" }, "source": [ "## 제로샷 토픽 분류" ] }, { "cell_type": "markdown", "metadata": { "id": "TR1wWKaZhc2e" }, "source": [ "마지막으로 제로샷 토픽 분류(zero-shot topic classification) 작업을 알아 보겠습니다. 제로샷 토픽 분류는 주어진 텍스트를 훈련에서 사용하지 않은 처음 본 클래스 레이블에 할당할 수 있습니다. 제로샷 토픽 분류 작업은 `'zero-topic'`으로 지정하며 자연어 추론과 마찬가지로 한국어, 영어, 중국어, 일본어를 지원합니다. 먼저 제로샷 토픽 분류를 위한 객체를 만들어 보겠습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "h5yacy5jhc2e" }, "outputs": [], "source": [ "zsl = Pororo(task=\"zero-topic\", lang='ko')" ] }, { "cell_type": "markdown", "metadata": { "id": "dOEFebuLhc2e" }, "source": [ "`zsl` 객체를 호출할 때 분류하려는 대상 문장을 첫 번째 매개변수로 전달하고 분류 토픽 리스트를 두 번째 매개변수로 전달합니다. 예를 들면 다음과 같습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "MXDGHWfGhc2f", "outputId": "5dcacad5-08cc-4134-92fd-aff280284e8a" }, "outputs": [ { "data": { "text/plain": [ "{'정치': 1.89, '사회': 10.9, '스포츠': 91.69, '연예': 37.32}" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zsl('손흥민이 골을 넣었다', ['정치', '사회', '스포츠', '연예'])" ] }, { "cell_type": "markdown", "metadata": { "id": "phDdmZ02hc2f" }, "source": [ "출력 결과를 보면 '스포츠'에 대한 점수가 높게 나왔으므로 올바르게 분류가 되었습니다. 사실 제로샷 토픽 분류는 이전에 살펴 보았던 자연어 처리 모델을 사용합니다.\n", "\n", "먼저 '손흥민이 골을 넣었다'와 '이 문장은 정치에 관한 것이다'라는 두 문장의 자연어 추론을 수행합니다. 그다음 나머지 '사회', '스포츠', '연예' 레이블에 대해서도 같은 작업을 반복하여 4번의 자연어 추론을 수행합니다. 각 수행 결과에서 중립(neutral)을 빼고 모순(contradiction)과 함의(entailment) 결과를 소프트맥스 함수를 통과시켜 확률로 변환합니다. `zsl` 객체가 반환한 결과는 이렇게 각 레이블에 대해 수행한 후 얻은 함의(entailment)에 대한 확률값입니다. " ] }, { "cell_type": "markdown", "metadata": { "id": "LErO5yqShc2f" }, "source": [ "제로샷 토픽 분류가 자연어 이해를 사용하므로 토픽에 국한하지 않고 첫 번째 문장이 어떤 상황에 관한 문장인지를 파악하는데 사용할 수도 있습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "cJMHwrANhc2f", "outputId": "6c5614e4-a635-46b1-d26a-4a182df937cc" }, "outputs": [ { "data": { "text/plain": [ "{'공격': 88.43, '수비': 18.86, '승리': 94.84, '패배': 4.25, '경기': 85.46, '실수': 24.96}" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zsl('손흥민이 골을 넣었다', ['공격', '수비', '승리', '패배', '경기', '실수'])" ] }, { "cell_type": "markdown", "metadata": { "id": "mtBUTjDnhc2f" }, "source": [ "제로샷 토픽 분류는 한글 외에 다른 언어에 대해서는 결과를 잘못 반환하는 버그가 있습니다. 자세한 내용은 깃허브 이슈([https://github.com/kakaobrain/pororo/issues/52](https://github.com/kakaobrain/pororo/issues/52))를 참고하세요.\n", "\n", "지금까지 `pororo`를 사용한 다양한 한글 처리 작업을 수행해 보았습니다. 이 라이브러리에 대한 더 자세한 내용은 깃허브와 온라인 문서를 참고하세요." ] } ], "metadata": { "colab": { "name": "pororo_nlp.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 1 }