{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using matplotlib backend: Qt5Agg\n", "Populating the interactive namespace from numpy and matplotlib\n", "CPython 3.5.6\n", "IPython 6.5.0\n", "\n", "sklearn 0.20.0\n", "konlpy 0.5.1\n", "pandas 0.23.4\n" ] } ], "source": [ "%pylab\n", "%load_ext watermark\n", "%watermark -v -p sklearn,konlpy,pandas" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import konlpy\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "데이터 파일을 읽어 리뷰 텍스트와 점수를 text_train, y_train 변수에 저장합니다. 데이터 파일의 내용은 번호, 텍스트, 레이블이 탭으로 구분되어 한 라인에 한개의 데이터 샘플이 들어 있습니다." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "df_train = pd.read_csv('data/ratings_train.txt', delimiter='\\t', keep_default_na=False)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
iddocumentlabel
09976970아 더빙.. 진짜 짜증나네요 목소리0
13819312흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나1
210265843너무재밓었다그래서보는것을추천한다0
39045019교도소 이야기구먼 ..솔직히 재미는 없다..평점 조정0
46483659사이몬페그의 익살스런 연기가 돋보였던 영화!스파이더맨에서 늙어보이기만 했던 커스틴 ...1
\n", "
" ], "text/plain": [ " id document label\n", "0 9976970 아 더빙.. 진짜 짜증나네요 목소리 0\n", "1 3819312 흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나 1\n", "2 10265843 너무재밓었다그래서보는것을추천한다 0\n", "3 9045019 교도소 이야기구먼 ..솔직히 재미는 없다..평점 조정 0\n", "4 6483659 사이몬페그의 익살스런 연기가 돋보였던 영화!스파이더맨에서 늙어보이기만 했던 커스틴 ... 1" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_train.head()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "text_train, y_train = df_train['document'].values, df_train['label'].values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "같은 방식으로 테스트 데이터를 읽습니다." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "df_test = pd.read_csv('data/ratings_test.txt', delimiter='\\t', keep_default_na=False)\n", "text_test = df_test['document'].values\n", "y_test = df_test['label'].values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "훈련 데이터와 테스트 데이터의 크기를 확인합니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(150000, array([75173, 74827]))" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(text_train), np.bincount(y_train)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(50000, array([24827, 25173]))" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(text_test), np.bincount(y_test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "KoNLPy 0.4.5 버전부터 `Twitter` 클래스가 `Okt` 클래스로 바뀌었습니다. [open-korean-text](https://github.com/open-korean-text/open-korean-text) 프로젝트는 [twitter-korean-text](https://github.com/twitter/twitter-korean-text) 프로젝트의 공식 포크입니다." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from konlpy.tag import Okt\n", "okt_tag = Okt()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`TfidfVectorizer` 의 토큰 파서를 대체하기 위해 `Okt` 클래스를 사용하는 함수를 만듭니다." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def okt_tokenizer(text):\n", " return okt_tag.morphs(text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`min_df`, `ngram_range`, `C` 매개변수를 대상으로 그리드 서치를 수행합니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "사이킷런 0.22 버전에서 `LogisticRegression` 클래스의 `solver` 매개변수 기본값이 `liblinear`에서 `lbfgs`로 변경될 예정입니다. 사이킷런 0.20 버전에서 `solver` 매개변수를 지정하지 않는 경우 이에 대한 경고 메세지가 출력됩니다. 경고 메세지를 피하기 위해 `solver` 매개변수 값을 `liblinear`로 지정합니다.\n", "\n", "사이킷런 0.22 버전에서 `GridSearchCV` 클래스의 `cv` 매개변수 기본값이 3에서 5로 바뀔 예정입니다. 0.20 버전에서 `cv` 매개변수를 지정하지 않는 경우 이에 관한 경고 메세지가 출력됩니다. 경고 메세지를 피하기 위해 `cv` 매개변수 값을 명시적으로 3으로 지정합니다." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "최상의 크로스 밸리데이션 점수: 0.704\n", "최적의 크로스 밸리데이션 파라미터: {'tfidfvectorizer__min_df': 3, 'tfidfvectorizer__ngram_range': (1, 1), 'logisticregression__C': 1}\n" ] } ], "source": [ "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.pipeline import make_pipeline\n", "from sklearn.model_selection import GridSearchCV\n", "\n", "okt_param_grid = {'tfidfvectorizer__min_df': [3, 5 ,7],\n", " 'tfidfvectorizer__ngram_range': [(1, 1), (1, 2), (1, 3)],\n", " 'logisticregression__C': [0.1, 1, 10]}\n", "okt_pipe = make_pipeline(TfidfVectorizer(tokenizer=okt_tokenizer), LogisticRegression(solver='liblinear'))\n", "okt_grid = GridSearchCV(okt_pipe, okt_param_grid, cv=3)\n", "\n", "# 그리드 서치를 수행합니다\n", "okt_grid.fit(text_train[0:1000], y_train[0:1000])\n", "print(\"최상의 크로스 밸리데이션 점수: {:.3f}\".format(okt_grid.best_score_))\n", "print(\"최적의 크로스 밸리데이션 파라미터: \", okt_grid.best_params_)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "테스트 세트 점수: 0.705\n" ] } ], "source": [ "X_test_okt = okt_grid.best_estimator_.named_steps[\"tfidfvectorizer\"].transform(text_test)\n", "score = okt_grid.best_estimator_.named_steps[\"logisticregression\"].score(X_test_okt, y_test)\n", "print(\"테스트 세트 점수: {:.3f}\".format(score))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "병렬 처리를 위해 `Mecab`을 사용하여 전체 데이터로 학습시킵니다. `Mecab`으로 토큰을 분할하는 함수를 만듭니다.\n", "\n", "최신 macOS Mojave에서는 `Mecab`에 필요한 jpype 라이브러리가 컴파일 오류가 발생합니다. 우분투에서 `n_jobs=1`을 사용해 실행했습니다." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from konlpy.tag import Mecab\n", "mecab = Mecab()\n", "def mecab_tokenizer(text):\n", " return mecab.morphs(text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "규제 파라미터의 범위를 확대하여 그리드 서치를 수행합니다." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "최상의 크로스 밸리데이션 점수: 0.870\n", "최적의 크로스 밸리데이션 파라미터: {'tfidfvectorizer__min_df': 3, 'tfidfvectorizer__ngram_range': (1, 3), 'logisticregression__C': 10}\n" ] } ], "source": [ "mecab_param_grid = {'tfidfvectorizer__min_df': [3, 5 ,7],\n", " 'tfidfvectorizer__ngram_range': [(1, 1), (1, 2), (1, 3)],\n", " 'logisticregression__C': [0.1, 1, 10, 100]}\n", "mecab_pipe = make_pipeline(TfidfVectorizer(tokenizer=mecab_tokenizer), LogisticRegression(solver='liblinear'))\n", "mecab_grid = GridSearchCV(mecab_pipe, mecab_param_grid, n_jobs=1, cv=3)\n", "\n", "# 그리드 서치를 수행합니다\n", "mecab_grid.fit(text_train, y_train)\n", "print(\"최상의 크로스 밸리데이션 점수: {:.3f}\".format(mecab_grid.best_score_))\n", "print(\"최적의 크로스 밸리데이션 파라미터: \", mecab_grid.best_params_)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "테스트 세트 점수: 0.875\n" ] } ], "source": [ "X_test_mecab = mecab_grid.best_estimator_.named_steps[\"tfidfvectorizer\"].transform(text_test)\n", "score = mecab_grid.best_estimator_.named_steps[\"logisticregression\"].score(X_test_mecab, y_test)\n", "print(\"테스트 세트 점수: {:.3f}\".format(score))" ] } ], "metadata": { "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.5.6" } }, "nbformat": 4, "nbformat_minor": 2 }