{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# **머신러닝을 활용한 전처리**\n",
    "규격화된 일정량의 데이터를 활용하여 많은 데이터 처리용 알고리즘\n",
    "- https://github.com/lovit/soynlp "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# **1 WPM (Word Piece Model)**\n",
    "- **[BPE(Byte Pair Encoding)](https://wikidocs.net/22592)** 알고리즘 (1994) Philip Gage 논문이 배경\n",
    "- **WPM** 모델링 기법 : 2016 년 **Pre Processing 모델링을** 활용한 Stemming 기법\n",
    "\n",
    "## **01 defaultDict 을 활용한 단어조합기**\n",
    "**[defaultDict()](https://itholic.github.io/python-defaultdict/)** 을 활용한 **N-Gram** 생성 후 **단어 조합하기** 연\n",
    "습\n",
    "1. 식별이 가능한 Token 단위로 학습데이터 만들기"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'l o w </w>'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 단어 Token 을 음절단위로 구분하기\n",
    "\" \".join(list(\"low\")+[\"</w>\"]) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "defaultdict(int,\n",
       "            {('l', 'o'): 7,\n",
       "             ('o', 'w'): 7,\n",
       "             ('w', '</w>'): 5,\n",
       "             ('w', 'e'): 8,\n",
       "             ('e', 's'): 2,\n",
       "             ('s', 't'): 2,\n",
       "             ('t', '</w>'): 2,\n",
       "             ('n', 'e'): 6,\n",
       "             ('e', 'w'): 6,\n",
       "             ('e', 'r'): 9,\n",
       "             ('r', '</w>'): 9,\n",
       "             ('w', 'i'): 3,\n",
       "             ('i', 'd'): 3,\n",
       "             ('d', 'e'): 3})"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Token 과 빈도수를 활용한 \n",
    "data = {\n",
    "    \" l o w </w>\":5,\n",
    "    \" l o w e s t </w>\":2,\n",
    "    \" n e w e r </w>\":6,\n",
    "    \" w i d e r </w>\":3,\n",
    "}\n",
    "\n",
    "from collections import defaultdict\n",
    "pair      = defaultdict(int)\n",
    "candidate = {}\n",
    "n_gram    = 2\n",
    "\n",
    "for k, v in data.items():\n",
    "    token = k.split()\n",
    "    for i in range(len(token)-1):\n",
    "        pair[tuple(token[ i : i+n_gram ])] += v\n",
    "pair"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('e', 'r')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# .get : Tuple 의 객체들을 따로 따로 묶어서 판단 합니다.\n",
    "maxKey = max(pair, key=pair.get) # pair.get : <function defaultdict.get>\n",
    "maxKey"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{' l o w </w>': 5,\n",
       " ' l o w e s t </w>': 2,\n",
       " ' n e w er </w>': 6,\n",
       " ' w i d er </w>': 3}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 함께 출현하는 빈도가 높을수록 단어로 묶일 확률이 높다\n",
    "# 작업결과 \"er\" 이 묶인걸 볼 수 있습니다.\n",
    "newData = {}\n",
    "import re\n",
    "for k, v in data.items():\n",
    "    newKey = re.sub(\" \".join(maxKey), \"\".join(maxKey), k)\n",
    "    newData[newKey] = v\n",
    "newData"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## **2 Word Piece Model 한글실습**\n",
    "- defaultDict 단어 token 을 만든 뒤,\n",
    "- Bi-Gram 빈도를 기준으로 단어 Token 을 찾기"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ngram defaultDict 만들기\n",
    "def findNgram(data):\n",
    "    pair = defaultdict(int)\n",
    "    candidate = {}\n",
    "    for k, v in data.items():\n",
    "        token = k.split()\n",
    "        for i in range(len(token)-1):\n",
    "            pair[tuple(token[ i : i+2 ])] += v\n",
    "    return pair\n",
    "\n",
    "# MaxKey 빈도값을 활용하여 단어 생성하기\n",
    "def mergeNgram(maxKey, data):\n",
    "    newData = {}\n",
    "    for k, v in data.items():\n",
    "        newKey = re.sub(\" \".join(maxKey), \"\".join(maxKey), k)\n",
    "        newData[newKey] = v\n",
    "    return newData"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{' 아버지 께 </w>': 5, ' 아버지 를 </w>': 2, ' 아버지 에 게 </w>': 6, ' 아버지 와 </w>': 3}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 어절 Token 목록을 활용한 단어 찾기\n",
    "data = {\n",
    "    \" 아 버 지 께 </w>\":5,\n",
    "    \" 아 버 지 를 </w>\":2,\n",
    "    \" 아 버 지 에 게 </w>\":6,\n",
    "    \" 아 버 지 와 </w>\":3,\n",
    "}\n",
    "\n",
    "for _ in range(2):\n",
    "    pairList = findNgram(data)\n",
    "    maxKey = max(pairList, key=pairList.get)\n",
    "    data = mergeNgram(maxKey, data)\n",
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## **3 Word Piece Model 한글실습 2**\n",
    "- 반복 횟수인 MaxKey 값을 자동으로 찾기"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_values([5, 2, 6, 3])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = {\n",
    "    \"아 버 지 께 </w>\":5,\n",
    "    \"아 버 지 를 </w>\":2,\n",
    "    \"아 버 지 에 게 </w>\":6,\n",
    "    \"아 버 지 와 </w>\":3,\n",
    "}\n",
    "data.values()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "def findNagram(data):\n",
    "    pair = defaultdict(int)\n",
    "    for k, v in data.items():\n",
    "        tokens = k.split()\n",
    "        for i in range(len(tokens)-1):\n",
    "            pair[tuple(tokens[i:i+2])] += v\n",
    "    return pair\n",
    "\n",
    "# maxValue 변수를 추가해 반복 횟수를 제한 합니다\n",
    "def mergeNgram(maxKey, data):\n",
    "    newData = dict()\n",
    "    for k, v in data.items():\n",
    "        newKey = re.sub(\" \".join(maxKey),\n",
    "                        \"\".join(maxKey), k)\n",
    "        newData[newKey] = v\n",
    "    return newData\n",
    "\n",
    "maxValue = max(data.values())\n",
    "maxValue"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('아', '버') 16\n",
      "('아버', '지') 16\n",
      "('아버지', '에') 6\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'아버지 께 </w>': 5, '아버지 를 </w>': 2, '아버지 에 게 </w>': 6, '아버지 와 </w>': 3}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for _ in range(1000):\n",
    "    pairList = findNagram(data)\n",
    "    maxKey   = max(pairList, key=pairList.get)\n",
    "    print(maxKey, pairList[maxKey])\n",
    "    \n",
    "    if pairList[maxKey] > maxValue:\n",
    "        data = mergeNgram(maxKey, data)\n",
    "    else: \n",
    "        break\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "defaultdict(int,\n",
       "            {('아버지', '께'): 5,\n",
       "             ('께', '</w>'): 5,\n",
       "             ('아버지', '를'): 2,\n",
       "             ('를', '</w>'): 2,\n",
       "             ('아버지', '에'): 6,\n",
       "             ('에', '게'): 6,\n",
       "             ('게', '</w>'): 6,\n",
       "             ('아버지', '와'): 3,\n",
       "             ('와', '</w>'): 3})"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pairList"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br/>\n",
    "\n",
    "# **2 레시피 데이터 적용하기**\n",
    "- **[BPE(Byte Pair Encoding)](https://wikidocs.net/22592)** 알고리즘 (1994) Philip Gage 논문이 배경\n",
    "- **WPM** 모델링 기법 : 2016 년 **Pre Processing 모델링을** 활용한 Stemming 기법\n",
    "\n",
    "## **01 defaultDict 을 활용한 단어조합기**\n",
    "**[defaultDict()](https://itholic.github.io/python-defaultdict/)** 을 활용한 **N-Gram** 생성 후 **단어 조합하기** 연\n",
    "습\n",
    "1. [f-string 사용법](https://bluese05.tistory.com/70\n",
    "1. 식별이 가능한 Token 단위로 학습데이터 만들기"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"tuple: ('Hi, I am', 'song', 123)\""
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 출처: https://bluese05.tistory.com/70 [ㅍㅍㅋㄷ]\n",
    "tuple = ('Hi, I am', 'song', 123)\n",
    "f'tuple: {tuple}' #>>> \"tuple: ('Hi, I am', 'song', 123)\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# r'' 은 백슬래시를 함수처리서 제외 함\n",
    "print(r\"머신러닝 \\n 알고리즘\")\n",
    "print(\"머신러닝 \\n 알고리즘\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "key = '' "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'a' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-18-84d555efa497>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34mf'{a} Python {b} tensorflow'\u001b[0m\u001b[0;31m#.(a=3.6, b=2)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'a' is not defined"
     ]
    }
   ],
   "source": [
    "f'{a} Python {b} tensorflow'#.(a=3.6, b=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def greet(k,v):\n",
    "    return f\"hello {k} in work {v}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "units = {\n",
    "    \"15ml\":[\"1큰술\",\"1T\",\"1Ts\",\"3t\"],\n",
    "    \"5ml\":[\"1작은술\",\"1t\",\"1ts\"],    \n",
    "    \"200ml\":[\"1컵\",\"1Cup\",\"1C\"],\n",
    "    \"250ml\":[\"16T\", \"1C\"],\n",
    "    \"180ml\":[\"1종이컵\"],\n",
    "    \"28.3g\":[\"1oz\"],\n",
    "    \"453g\":[\"1파운드\",\"lb\"],\n",
    "    \"3780ml\":[\"1갤런\",\"gallon\"],\n",
    "    \"2g\":[\"1꼬집\",'약간'],\n",
    "    \"4g\":[\"조금\"],\n",
    "    \"6g\":[\"조금\"],\n",
    "    \"10g\":[\"적당량\"],\n",
    "    \"50g\":[\"1줌\"], # 나물\n",
    "    \"100g\":[\"1큰줌\"], # 나물\n",
    "    \"13마리\":[\"1줌\"], # 멸치\n",
    "    \"26마리\":[\"1큰줌\"], # 멸치\n",
    "    \"100g\":[\"1주먹\"], # 여자 어른의 주먹크기\n",
    "    \"3cm\":[\"1토막\"],\n",
    "    \"1알\":[\"1톨\",\"1쪽\"], # 마늘, 생강 등\n",
    "    \"600g\":[\"1근\"], # 고기\n",
    "    \"400g\":[\"1근\"], # 채소\n",
    "    \"200g\":[\"1봉지\"],\n",
    "}\n",
    "\n",
    "unit_tokens = []\n",
    "for _ in [v  for k,v in units.items()]:\n",
    "    unit_tokens += _\n",
    "\", \".join(sorted(set(unit_tokens)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# **식약처 레시피로 이름구분 및 단위 기준 전처리**\n",
    "레시피 데이터 전처리 및 단위기준 일치사전 만들기\n",
    "```\n",
    "계량법 안내\n",
    "1큰술(1T, 1Ts) = 1숟가락 \t15ml = 3t (밥숟가락 뜨면 1큰술)\n",
    "1작은술(1t, 1ts)              5ml (티스푼으로는 2스푼이 1작은술)\n",
    "1컵(1Cup, 1C) \t200ml = 16T (한국,중국,일본)  (서양(미국)은 1C가 240~250ml)\n",
    "1종이컵 \t180ml; 1oz \t28.3g\n",
    "1파운드(lb) \t약 0.453 킬로그램(kg)\n",
    "1갤런(gallon) \t약 3.78 리터(ℓ)\n",
    "1꼬집 \t약 2g 정도이며 '약간'이라고 표현하기도 함\n",
    "조금   \t약간의 2~3배\n",
    "적당량 \t기호에 따라 마음대로 조절해서 넣으란 표현\n",
    "1줌 \t    한손 가득 (예시 : 멸치 1줌 = 국멸치인 경우 12~15마리, 나물 1줌은 50g) 크게 1줌 = 2줌 [1줌의 두배]\n",
    "1주먹 \t여자 어른의 주먹크기, 고기로는 100g\n",
    "1토막 \t2~3cm두께 정도의 분량\n",
    "마늘 1톨 \t깐 마늘 한쪽\n",
    "생강 1쪽 \t마늘 1톨의 크기와 비슷\n",
    "생강 1톨 \t아기 손바닥만한 크기의 통생강 1개\n",
    "고기 1근 \t600g\n",
    "채소 1근 \t400g\n",
    "채소 1봉지 \t200g 정도\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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}