{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "

\n", "# **Chapter 3 | word2vec**\n", "## **1 데이터 전처리**\n", "[**word2vec matplotlib**](https://www.kaggle.com/jeffd23/visualizing-word-vectors-with-t-sne)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ! apt-get update\n", "# ! apt-get install g++ openjdk-8-jdk \n", "# ! pip3 install nltk konlpy matplotlib gensim \n", "\n", "# ! apt-get install fonts-nanum-eco\n", "# ! apt-get install fontconfig\n", "# ! fc-cache -fv\n", "# ! cp /usr/share/fonts/truetype/nanum/Nanum* /usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/\n", "# ! rm -rf /content/.cache/matplotlib/*\n", "\n", "# script_text = \"https://raw.githubusercontent.com/YongBeomKim/nltk_tutorial/master/data/movie_memories_of_murder_2003.txt\"\n", "# font_file = \"/usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/NanumGothicCoding.ttf\"\n", "script_text = \"../data/movie_memories_of_murder_2003.txt\"\n", "font_file = \"../data/D2Coding.ttf\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib as mpl \n", "import matplotlib.pyplot as plt\n", "import matplotlib.font_manager as fm\n", "\n", "font_name = fm.FontProperties(fname=font_file, size=10).get_name()\n", "plt.rc('font', family=font_name)\n", "fm._rebuild()\n", "mpl.rcParams['axes.unicode_minus'] = False" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "from sklearn.manifold import TSNE\n", "import matplotlib.pyplot as plt\n", "\n", "def tsne_plot(model, figsize=(12,12)):\n", " \"Creates and TSNE model and plots it\"\n", " labels, tokens = [], []\n", " for word in model.wv.vocab:\n", " tokens.append(model[word])\n", " labels.append(word)\n", "\n", " tsne_model = TSNE(n_components=2)\n", " new_values = tsne_model.fit_transform(tokens)\n", "\n", " x, y = [], []\n", " for value in new_values:\n", " x.append(value[0])\n", " y.append(value[1])\n", "\n", " plt.figure(figsize=figsize) \n", " for i in range(len(x)):\n", " plt.scatter(x[i],y[i])\n", " plt.annotate(labels[i],\n", " xy = (x[i], y[i]),\n", " fontsize=15)\n", "# ha = 'right',\n", "# va = 'bottom')\n", " plt.grid(True)\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 텍스트를 줄단위로 끊어서 불러온뒤\n", "# Token 단위로, 한글명사들을 추출한다\n", "def txtnoun(sentences , skip=False, tags=['Noun'], stem=True, set_tokens=False):\n", "\n", " r\"\"\"\n", " 살인의 추억 대본의 텍스트 전처리 작업을 진행합니다\n", " :param sentences: 단일한 Text String 데이터를 입력합니다\n", " :param skip: 분류된 Token 중 사용자가 원하는 형태로 변환된 내용을 출력\n", " :param tags: konlpy 로 분류된 품사중 추출하고자 하는 품사를 정의합니다\n", " :param stem: stemming 작업여부를 정의합니다.\n", " :param set_tokens: return 결과를 token list 객체로 출력할지를 정의합니다\n", " :return: set_tokens 내용에 따라 List, String 타입으로 출력합니다\n", " \"\"\"\n", " \n", " import re\n", " from konlpy.tag import Okt\n", " twitter = Okt()\n", " result = []\n", " sentences = sentences.replace('\\n', '\\n|')\n", " sentences = sentences.split('|')\n", " for content in sentences:\n", " texts = content.replace('\\n', '') # 해당줄의 줄바꿈 내용 제거\n", " tokenizer = re.compile(r'[^ ㄱ-힣]+') # 한글과 띄어쓰기를 제외한 모든 글자를 선택\n", " token_data = tokenizer.sub('', texts) # 한글과 띄어쓰기를 제외한 모든 부분을 제거\n", " token_data = token_data.split(' ')\n", " sentence = []\n", "\n", " for token in token_data:\n", " # skip 대상이 없을 떄\n", " if skip == False:\n", " chk_tok = twitter.pos(token, stem=stem)\n", " chk_tok = [temp[0] for temp in chk_tok if temp[1] in tags]\n", " ckeck = \"\".join(chk_tok)\n", " if len(ckeck) > 1:\n", " sentence.append(ckeck)\n", "\n", " # skip 내용이 있을 때\n", " else:\n", " if token.strip() in skip.keys():\n", " result.append(skip[token.strip()])\n", " else:\n", " chk_tok = twitter.pos(token, stem=stem)\n", " chk_tok = [temp[0] for temp in chk_tok if temp[1] in tags]\n", " ckeck = \"\".join(chk_tok)\n", "\n", " # 전처리가 끝난 결과가 skip에 해당여부 판단\n", " if ckeck.strip() in skip.keys():\n", " result.append(skip[ckeck.strip()])\n", " elif len(ckeck) > 1:\n", " sentence.append(ckeck)\n", "\n", " # 단락별 작업이 끝난 뒤 '\\n'를 덧붙여서 작업을 종료\n", " temp = \"\".join(sentence)\n", " if len(temp) > 1:\n", " sentence = \" \".join(sentence)\n", " sentence += \"\\n\"\n", " result.append(sentence)\n", "\n", " if set_tokens == True:\n", " from nltk.tokenize import word_tokenize\n", " set_token = word_tokenize(\" \".join(result))\n", " return list(set(set_token))\n", "\n", " else:\n", " return \" \".join(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "skips = {'두만':'박두만', '태윤':'서태윤', '용구':'조용구', '귀옥':'권귀옥', \n", " '희봉':'구희봉', '동철':'신동철', '광호':'백광호', '병순':'조병순', \n", " '해일':'박해일', '광호의':'백광호', '백광호의':'백광호'}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# import requests\n", "# sentences = requests.get(script_text).text\n", "# sentences[:300]\n", "\n", "with open(script_text, 'r') as f:\n", " sentences = f.read()\n", "sentences[:300]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sentences = txtnoun(sentences, skip=skips, tags=['Noun'])\n", "script_file = 'script.txt'\n", "with open(script_file, 'w', encoding='utf-8') as file:\n", " file.write(sentences)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "from gensim.models import word2vec\n", "data = word2vec.LineSentence(script_file)\n", "model = word2vec.Word2Vec(data, size=30, window=2, min_count=10, \n", " hs=1, workers=4, iter=100, sg=1)\n", "model_file = \"script.model\"\n", "model.save(model_file)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "## **2 Word2Vec 모델의 활용**\n", "모델을 활용하여 유력한 범인을 찾아보자!!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 저장된 학습모델파일 불러오기\n", "from gensim.models import word2vec\n", "model_file = \"script.model\"\n", "model = word2vec.Word2Vec.load(model_file)\n", "len(model.wv.vocab.keys())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model.wv.vocab.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "## **3 Word2Vec 모델 내용 확인**\n", "모델을 활용하여 유력한 범인을 찾아보자!!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 범인과 관련된 내용 중 사람이름이 안나옴...\n", "model.wv.most_similar('범인', topn=10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 현장과 가장 가깝게 등장한 인물이 1명 등장\n", "model.wv.most_similar('현장', topn=10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 현장 과 백광호 와 밀접한 증거들 중에 방해가 되는 내용을 찾는다\n", "model.wv.most_similar(['현장','이향숙'], topn=10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 현장 과 백광호 와 밀접한 증거들 중에 방해가 되는 내용을 찾는다\n", "model.wv.most_similar(['현장', '백광호'], topn=10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 현장 과 백광호 와 밀접한 증거들 중에 '참깨밭' 이 계속 방해가 됨\n", "# 참깨밭에 백광호가 밀접하게 연결되어 있어서 이를 제외한 분석이 필요\n", "model.wv.most_similar(['현장','백광호'], negative=['참깨밭'], topn=15)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 현장 과 백광호 와 밀접한 증거들 중에 '참깨밭' 이 계속 방해가 됨\n", "# 참깨밭에 백광호와 이향숙을 제외한 분석이 필요\n", "model.wv.most_similar(['현장','백광호'], negative=['참깨밭','이향숙'], topn=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "## **4 Visulaization**\n", "gensim" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vocab = list(model.wv.vocab)\n", "X = model[vocab]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.manifold import TSNE\n", "tsne = TSNE(n_components = 2)\n", "X_tsne = tsne.fit_transform(X)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "df = pd.DataFrame(X_tsne, index=vocab, columns=['x','y'])\n", "df.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "fig = plt.figure(figsize=(12,12))\n", "ax = fig.add_subplot(1, 1, 1)\n", "ax.scatter(df['x'], df['y'])\n", "for word, pos in df.iterrows():\n", " ax.annotate(word, pos, fontsize=15)\n", "plt.grid(True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tsne_plot(model)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "## **5 결과의 해석 및 활용**\n", "1. 유사관계, 반대관계로 **출력된 모든 Token들이 가치가 있지는 않다**\n", "1. **min_count, Vector 갯수** 2가지 조건만 사용하여 모델을 생성한다\n", "1. Word2Vec 도 **문서를 분석하는 도구**에 불과 (절대적 **가치를 창출하는 기법** 으로 오해 X)\n", "1. 해당 분야의 **잘 정리된 Document로 학습한 Word2Vec 모델** 에서 **유의미 한 token 들의 관계** 를 Template으로 잘 정리\n", "1. 분석대상 문서를 **유사한 조건으로 word2vec 모델** 을 만들고, **앞에서 정리된 Template와** 비교하여 결과\n", "다듬기\n", "1. 시나리오의 분석 경우\n", " 1. 결과물 중 인물의 Token 만 활용하여 분석\n", " 1. 결과물 중 증거물의 Token 만 활용하여 분석\n", " 1. 결과물 중 장소의 Token 만 활용하여 분석" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# model 에 등장하는 인물들\n", "charator = [\"박두만\", \"서태윤\", \"조용구\", \"권귀옥\", \"구희봉\", \"신동철\", \"백광호\",\n", " \"조병순\", \"박해일\", \"박보희\", \"이향숙\", \"독고현순\", \"박명자\", \"안미선\", \n", " \"반장\", \"소현\", \"범인\", \"형사\", '괴남자', '순경','피해자', '권기옥','용의자']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# model 에 등장하는 장소명 들\n", "area = ['현장', '사무실', '취조실', '변소', '참깨밭', '빗줄기', '어둠속', '언덕집']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# model 에 등장하는 Item 들\n", "items = ['브래지어', '팬티', '우산', '운동화', '스타킹', '목소리', '불빛', '음악', '후레쉬', \n", " '카메라', '라디오', '방송', '유전자', '가방', '코피', '휴지', '신문', '총구']" ] } ], "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.6.8" } }, "nbformat": 4, "nbformat_minor": 4 }