{"generator":"Jekyll","link":[{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/feed.xml","rel":"self","type":"application\/atom+xml"}},{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/","rel":"alternate","type":"text\/html","hreflang":"en, ru"}}],"updated":"2022-08-10T10:53:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/feed.xml","title":"Natural Language Processing","subtitle":"Tatiana Shavrina's blog about NLP\n","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"entry":[{"title":"How to Make a Youtube Comment Corpus (without API)","link":{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/2020\/01\/08\/youtube\/","rel":"alternate","type":"text\/html","title":"How to Make a Youtube Comment Corpus (without API)"}},"published":"2020-01-08T00:00:00+00:00","updated":"2020-01-08T00:00:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/2020\/01\/08\/youtube","content":"<p><strong>All ways to do it<\/strong><\/p>\n\n<p>Of course, Youtube has its own useful <a href=\"https:\/\/developers.google.com\/youtube\/v3\">API<\/a><\/p>\n\n<p>But if you are reading this article, you probably already know it has strict limitations, including the number of comments collected per day.<\/p>\n\n<p><img src=\"https:\/\/www.meme-arsenal.com\/memes\/a8e9e3a78cff3da2e810795503d9021b.jpg\" alt=\"\" \/><\/p>\n\n<p>If you are a researcher who would like to collect a small dataset, it is possible to reproduce all the manual behavour in a way described below.<\/p>\n\n<h2 id=\"collecting-comments\">Collecting Comments<\/h2>\n\n<p>Since we aren\u2019t using proposed functionality, the script should pretend it is a user scrolling through the trends.<\/p>\n\n<p>Our solution will be based on <strong>Selenium + CSSSelector<\/strong> - let\u2019s start!<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import<\/span> <span class=\"nn\">os<\/span><span class=\"p\">,<\/span> <span class=\"n\">sys<\/span><span class=\"p\">,<\/span> <span class=\"n\">time<\/span><span class=\"p\">,<\/span> <span class=\"n\">datetime<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">json<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">requests<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">lxml.html<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">io<\/span><span class=\"p\">,<\/span> <span class=\"n\">codecs<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">tqdm<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">tqdm<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">lxml.cssselect<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">CSSSelector<\/span>\n<span class=\"c1\">#to make the Browser Working\n<\/span><span class=\"kn\">from<\/span> <span class=\"nn\">selenium<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">webdriver<\/span>\n<span class=\"c1\">#Send keycodes to Elements\n<\/span><span class=\"kn\">from<\/span> <span class=\"nn\">selenium.webdriver.common.keys<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Keys<\/span>\n<span class=\"c1\">#scrape the url's and comments\n<\/span><span class=\"kn\">from<\/span> <span class=\"nn\">bs4<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">BeautifulSoup<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"selenium---rolling-through-trends\">Selenium - rolling through trends<\/h3>\n\n<p>Firstly, we will scroll the trends page with Selenium. \nYou should have Firefox installed to use the code below \u2013 or change for <a href=\"https:\/\/selenium-python.readthedocs.io\/api.html\">your browser<\/a>.<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># The List where the links to the videos are stored\n<\/span><span class=\"n\">links<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">comments<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">list<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">homePage<\/span> <span class=\"o\">=<\/span> <span class=\"s\">'https:www.youtube.com'<\/span>\n<span class=\"n\">linksSize<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">10<\/span>\n<span class=\"n\">driver<\/span> <span class=\"o\">=<\/span> <span class=\"n\">webdriver<\/span><span class=\"p\">.<\/span><span class=\"n\">Firefox<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">output_path<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"p\">.<\/span><span class=\"n\">path<\/span><span class=\"p\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"s\">\"\/media\/user\/youtube\/\"<\/span><span class=\"p\">,<\/span> <span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">datetime<\/span><span class=\"p\">.<\/span><span class=\"n\">date<\/span><span class=\"p\">.<\/span><span class=\"n\">today<\/span><span class=\"p\">()),<\/span><span class=\"s\">\".txt\"<\/span><span class=\"p\">)<\/span>\n \n<span class=\"k\">def<\/span> <span class=\"nf\">loadFullPage<\/span><span class=\"p\">(<\/span><span class=\"n\">Timeout<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">reachedbottom<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">None<\/span>\n    <span class=\"k\">while<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">reachedbottom<\/span><span class=\"p\">:<\/span>\n        <span class=\"c1\">#scroll one page down\n<\/span>        <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">execute_script<\/span><span class=\"p\">(<\/span><span class=\"s\">\"window.scrollTo(0,Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,document.documentElement.clientHeight));\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"n\">time<\/span><span class=\"p\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"n\">Timeout<\/span><span class=\"p\">)<\/span>\n        <span class=\"c1\">#check if the bottom is reached\n<\/span>        <span class=\"n\">a<\/span> <span class=\"o\">=<\/span> <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">execute_script<\/span><span class=\"p\">(<\/span><span class=\"s\">\"return document.documentElement.scrollTop;\"<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">b<\/span> <span class=\"o\">=<\/span> <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">execute_script<\/span><span class=\"p\">(<\/span><span class=\"s\">\"return document.documentElement.scrollHeight - document.documentElement.clientHeight;\"<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">relativeHeight<\/span> <span class=\"o\">=<\/span> <span class=\"n\">a<\/span> <span class=\"o\">\/<\/span> <span class=\"n\">b<\/span>\n        <span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"n\">relativeHeight<\/span><span class=\"o\">==<\/span><span class=\"mi\">1<\/span><span class=\"p\">):<\/span>\n            <span class=\"n\">reachedbottom<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">True<\/span>\n<span class=\"k\">def<\/span> <span class=\"nf\">getComments<\/span><span class=\"p\">(<\/span><span class=\"n\">link<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">url<\/span><span class=\"o\">=<\/span><span class=\"s\">'https:youtube.com'<\/span><span class=\"o\">+<\/span><span class=\"n\">link<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">loadFullPage<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">main<\/span><span class=\"p\">():<\/span>\n    <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">url<\/span><span class=\"o\">=<\/span><span class=\"n\">homePage<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">enoughLinks<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">None<\/span>\n    <span class=\"k\">while<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">enoughLinks<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">loadFullPage<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">soup<\/span> <span class=\"o\">=<\/span> <span class=\"n\">BeautifulSoup<\/span><span class=\"p\">(<\/span><span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">page_source<\/span><span class=\"p\">,<\/span> <span class=\"s\">'html.parser'<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">link<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">soup<\/span><span class=\"p\">.<\/span><span class=\"n\">find_all<\/span><span class=\"p\">(<\/span><span class=\"s\">\"a\"<\/span><span class=\"p\">,<\/span><span class=\"n\">class_<\/span><span class=\"o\">=<\/span><span class=\"s\">\"yt-simple-endpoint style-scope ytd-grid-video-renderer\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">href<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">link<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">links<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">links<\/span><span class=\"p\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">link<\/span><span class=\"p\">[<\/span><span class=\"s\">'href'<\/span><span class=\"p\">])<\/span>\n        <span class=\"k\">if<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">links<\/span><span class=\"p\">)<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">linksSize<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">driver<\/span><span class=\"p\">.<\/span><span class=\"n\">refresh<\/span><span class=\"p\">()<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"c1\">#for i in range(len(links)-1000):\n<\/span>                <span class=\"c1\">#links.pop()\n<\/span>            <span class=\"n\">enoughLinks<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">True<\/span>\n    <span class=\"n\">output<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">output_path<\/span><span class=\"p\">,<\/span> <span class=\"s\">'a'<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">link<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">links<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">output<\/span><span class=\"p\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">link<\/span><span class=\"o\">+<\/span><span class=\"s\">\"<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">output<\/span><span class=\"p\">.<\/span><span class=\"n\">close<\/span><span class=\"p\">()<\/span>\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">links<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">if<\/span> <span class=\"n\">__name__<\/span> <span class=\"o\">==<\/span> <span class=\"s\">'__main__'<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">main<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"cssselector---extracting-comments\">CSSSelector - extracting comments<\/h3>\n\n<p>Now as we have saved the unique ids of the videos in trends, we will start to gather comments with requests and CSSSelector.\nCSSSelector is quite optional here, you can use either lxml or regular expressions, but the library in concern IMHO is most convenient for collecting the trees of comments.<\/p>\n\n<p>We are using user agent still pretending to be Mozilla Firefox.<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">YOUTUBE_COMMENTS_URL<\/span> <span class=\"o\">=<\/span> <span class=\"s\">'https:\/\/www.youtube.com\/all_comments?v={youtube_id}'<\/span>\n<span class=\"n\">YOUTUBE_COMMENTS_AJAX_URL<\/span> <span class=\"o\">=<\/span> <span class=\"s\">'https:\/\/www.youtube.com\/comment_ajax'<\/span>\n<span class=\"n\">USER_AGENT<\/span> <span class=\"o\">=<\/span> <span class=\"s\">'Mozilla\/5.0 (Windows NT 6.1; WOW64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/48.0.2564.116 Safari\/537.36'<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">find_value<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">,<\/span> <span class=\"n\">key<\/span><span class=\"p\">,<\/span> <span class=\"n\">num_chars<\/span><span class=\"o\">=<\/span><span class=\"mi\">2<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">pos_begin<\/span> <span class=\"o\">=<\/span> <span class=\"n\">html<\/span><span class=\"p\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">key<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">key<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"n\">num_chars<\/span>\n    <span class=\"n\">pos_end<\/span> <span class=\"o\">=<\/span> <span class=\"n\">html<\/span><span class=\"p\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"s\">'\"'<\/span><span class=\"p\">,<\/span> <span class=\"n\">pos_begin<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">html<\/span><span class=\"p\">[<\/span><span class=\"n\">pos_begin<\/span><span class=\"p\">:<\/span> <span class=\"n\">pos_end<\/span><span class=\"p\">]<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">extract_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">tree<\/span> <span class=\"o\">=<\/span> <span class=\"n\">lxml<\/span><span class=\"p\">.<\/span><span class=\"n\">html<\/span><span class=\"p\">.<\/span><span class=\"n\">fromstring<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">item_sel<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CSSSelector<\/span><span class=\"p\">(<\/span><span class=\"s\">'.comment-item'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">text_sel<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CSSSelector<\/span><span class=\"p\">(<\/span><span class=\"s\">'.comment-text-content'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">time_sel<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CSSSelector<\/span><span class=\"p\">(<\/span><span class=\"s\">'.time'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">author_sel<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CSSSelector<\/span><span class=\"p\">(<\/span><span class=\"s\">'.user-name'<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">for<\/span> <span class=\"n\">item<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">item_sel<\/span><span class=\"p\">(<\/span><span class=\"n\">tree<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">yield<\/span> <span class=\"p\">{<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">:<\/span> <span class=\"n\">item<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s\">'data-cid'<\/span><span class=\"p\">),<\/span>\n               <span class=\"s\">'text'<\/span><span class=\"p\">:<\/span> <span class=\"n\">text_sel<\/span><span class=\"p\">(<\/span><span class=\"n\">item<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">].<\/span><span class=\"n\">text_content<\/span><span class=\"p\">(),<\/span>\n               <span class=\"s\">'time'<\/span><span class=\"p\">:<\/span> <span class=\"n\">time_sel<\/span><span class=\"p\">(<\/span><span class=\"n\">item<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">].<\/span><span class=\"n\">text_content<\/span><span class=\"p\">().<\/span><span class=\"n\">strip<\/span><span class=\"p\">(),<\/span>\n               <span class=\"s\">'author'<\/span><span class=\"p\">:<\/span> <span class=\"n\">author_sel<\/span><span class=\"p\">(<\/span><span class=\"n\">item<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">].<\/span><span class=\"n\">text_content<\/span><span class=\"p\">()}<\/span>\n\n\n<span class=\"k\">def<\/span> <span class=\"nf\">extract_reply_cids<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">tree<\/span> <span class=\"o\">=<\/span> <span class=\"n\">lxml<\/span><span class=\"p\">.<\/span><span class=\"n\">html<\/span><span class=\"p\">.<\/span><span class=\"n\">fromstring<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">sel<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CSSSelector<\/span><span class=\"p\">(<\/span><span class=\"s\">'.comment-replies-header &gt; .load-comments'<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s\">'data-cid'<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">sel<\/span><span class=\"p\">(<\/span><span class=\"n\">tree<\/span><span class=\"p\">)]<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">ajax_request<\/span><span class=\"p\">(<\/span><span class=\"n\">session<\/span><span class=\"p\">,<\/span> <span class=\"n\">url<\/span><span class=\"p\">,<\/span> <span class=\"n\">params<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"p\">,<\/span> <span class=\"n\">retries<\/span><span class=\"o\">=<\/span><span class=\"mi\">10<\/span><span class=\"p\">,<\/span> <span class=\"n\">sleep<\/span><span class=\"o\">=<\/span><span class=\"mi\">20<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">_<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">retries<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"n\">session<\/span><span class=\"p\">.<\/span><span class=\"n\">post<\/span><span class=\"p\">(<\/span><span class=\"n\">url<\/span><span class=\"p\">,<\/span> <span class=\"n\">params<\/span><span class=\"o\">=<\/span><span class=\"n\">params<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">response<\/span><span class=\"p\">.<\/span><span class=\"n\">status_code<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">200<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">response_dict<\/span> <span class=\"o\">=<\/span> <span class=\"n\">json<\/span><span class=\"p\">.<\/span><span class=\"n\">loads<\/span><span class=\"p\">(<\/span><span class=\"n\">response<\/span><span class=\"p\">.<\/span><span class=\"n\">text<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">return<\/span> <span class=\"n\">response_dict<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s\">'page_token'<\/span><span class=\"p\">,<\/span> <span class=\"bp\">None<\/span><span class=\"p\">),<\/span> <span class=\"n\">response_dict<\/span><span class=\"p\">[<\/span><span class=\"s\">'html_content'<\/span><span class=\"p\">]<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">time<\/span><span class=\"p\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"n\">sleep<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">download_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">youtube_id<\/span><span class=\"p\">,<\/span> <span class=\"n\">sleep<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">session<\/span> <span class=\"o\">=<\/span> <span class=\"n\">requests<\/span><span class=\"p\">.<\/span><span class=\"n\">Session<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">session<\/span><span class=\"p\">.<\/span><span class=\"n\">headers<\/span><span class=\"p\">[<\/span><span class=\"s\">'User-Agent'<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">USER_AGENT<\/span>\n\n    <span class=\"c1\"># Get Youtube page with initial comments\n<\/span>    <span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"n\">session<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">YOUTUBE_COMMENTS_URL<\/span><span class=\"p\">.<\/span><span class=\"nb\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">youtube_id<\/span><span class=\"o\">=<\/span><span class=\"n\">youtube_id<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">html<\/span> <span class=\"o\">=<\/span> <span class=\"n\">response<\/span><span class=\"p\">.<\/span><span class=\"n\">text<\/span>\n    <span class=\"n\">reply_cids<\/span> <span class=\"o\">=<\/span> <span class=\"n\">extract_reply_cids<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">ret_cids<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">comment<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">extract_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">ret_cids<\/span><span class=\"p\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">comment<\/span><span class=\"p\">[<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">])<\/span>\n        <span class=\"k\">yield<\/span> <span class=\"n\">comment<\/span>\n\n    <span class=\"n\">page_token<\/span> <span class=\"o\">=<\/span> <span class=\"n\">find_value<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">,<\/span> <span class=\"s\">'data-token'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">session_token<\/span> <span class=\"o\">=<\/span> <span class=\"n\">find_value<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">,<\/span> <span class=\"s\">'XSRF_TOKEN'<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">first_iteration<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">True<\/span>\n    <span class=\"c1\"># Get remaining comments (the same as pressing the 'Show more' button)\n<\/span>    <span class=\"k\">while<\/span> <span class=\"n\">page_token<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"s\">'video_id'<\/span><span class=\"p\">:<\/span> <span class=\"n\">youtube_id<\/span><span class=\"p\">,<\/span>\n                <span class=\"s\">'session_token'<\/span><span class=\"p\">:<\/span> <span class=\"n\">session_token<\/span><span class=\"p\">}<\/span>\n\n        <span class=\"n\">params<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"s\">'action_load_comments'<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n                  <span class=\"s\">'order_by_time'<\/span><span class=\"p\">:<\/span> <span class=\"bp\">True<\/span><span class=\"p\">,<\/span>\n                  <span class=\"s\">'filter'<\/span><span class=\"p\">:<\/span> <span class=\"n\">youtube_id<\/span><span class=\"p\">}<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">first_iteration<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">params<\/span><span class=\"p\">[<\/span><span class=\"s\">'order_menu'<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">True<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"s\">'page_token'<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">page_token<\/span>\n        <span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ajax_request<\/span><span class=\"p\">(<\/span><span class=\"n\">session<\/span><span class=\"p\">,<\/span> <span class=\"n\">YOUTUBE_COMMENTS_AJAX_URL<\/span><span class=\"p\">,<\/span> <span class=\"n\">params<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">response<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">break<\/span>\n        <span class=\"n\">page_token<\/span><span class=\"p\">,<\/span> <span class=\"n\">html<\/span> <span class=\"o\">=<\/span> <span class=\"n\">response<\/span>\n        <span class=\"n\">reply_cids<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">extract_reply_cids<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">comment<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">extract_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">comment<\/span><span class=\"p\">[<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">]<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">ret_cids<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">ret_cids<\/span><span class=\"p\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">comment<\/span><span class=\"p\">[<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">])<\/span>\n                <span class=\"k\">yield<\/span> <span class=\"n\">comment<\/span>\n\n        <span class=\"n\">first_iteration<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">False<\/span>\n        <span class=\"n\">time<\/span><span class=\"p\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"n\">sleep<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"c1\"># Get replies (the same as pressing the 'View all X replies' link)\n<\/span>    <span class=\"k\">for<\/span> <span class=\"n\">cid<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">reply_cids<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"s\">'comment_id'<\/span><span class=\"p\">:<\/span> <span class=\"n\">cid<\/span><span class=\"p\">,<\/span>\n                <span class=\"s\">'video_id'<\/span><span class=\"p\">:<\/span> <span class=\"n\">youtube_id<\/span><span class=\"p\">,<\/span>\n                <span class=\"s\">'can_reply'<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n                <span class=\"s\">'session_token'<\/span><span class=\"p\">:<\/span> <span class=\"n\">session_token<\/span><span class=\"p\">}<\/span>\n        <span class=\"n\">params<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"s\">'action_load_replies'<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n                  <span class=\"s\">'order_by_time'<\/span><span class=\"p\">:<\/span> <span class=\"bp\">True<\/span><span class=\"p\">,<\/span>\n                  <span class=\"s\">'filter'<\/span><span class=\"p\">:<\/span> <span class=\"n\">youtube_id<\/span><span class=\"p\">,<\/span>\n                  <span class=\"s\">'tab'<\/span><span class=\"p\">:<\/span> <span class=\"s\">'inbox'<\/span><span class=\"p\">}<\/span>\n        <span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ajax_request<\/span><span class=\"p\">(<\/span><span class=\"n\">session<\/span><span class=\"p\">,<\/span> <span class=\"n\">YOUTUBE_COMMENTS_AJAX_URL<\/span><span class=\"p\">,<\/span> <span class=\"n\">params<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">response<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">break<\/span>\n        <span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">html<\/span> <span class=\"o\">=<\/span> <span class=\"n\">response<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">comment<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">extract_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">html<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">comment<\/span><span class=\"p\">[<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">]<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">ret_cids<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">ret_cids<\/span><span class=\"p\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">comment<\/span><span class=\"p\">[<\/span><span class=\"s\">'cid'<\/span><span class=\"p\">])<\/span>\n                <span class=\"k\">yield<\/span> <span class=\"n\">comment<\/span>\n        <span class=\"n\">time<\/span><span class=\"p\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"n\">sleep<\/span><span class=\"p\">)<\/span>\n\n\n<span class=\"k\">def<\/span> <span class=\"nf\">comments_main<\/span><span class=\"p\">(<\/span><span class=\"n\">youtube_id<\/span><span class=\"p\">,<\/span> <span class=\"n\">output<\/span><span class=\"p\">,<\/span> <span class=\"n\">limit<\/span><span class=\"o\">=<\/span><span class=\"mi\">100<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">youtube_id<\/span> <span class=\"ow\">or<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">output<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">parser<\/span><span class=\"p\">.<\/span><span class=\"n\">print_usage<\/span><span class=\"p\">()<\/span>\n            <span class=\"k\">raise<\/span> <span class=\"nb\">ValueError<\/span><span class=\"p\">(<\/span><span class=\"s\">'you need to specify a Youtube ID and an output filename'<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'Downloading Youtube comments for video:'<\/span><span class=\"p\">,<\/span> <span class=\"n\">youtube_id<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">count<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n        <span class=\"k\">with<\/span> <span class=\"n\">io<\/span><span class=\"p\">.<\/span><span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">output<\/span><span class=\"p\">,<\/span> <span class=\"s\">'w'<\/span><span class=\"p\">,<\/span> <span class=\"n\">encoding<\/span><span class=\"o\">=<\/span><span class=\"s\">'utf8'<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">fp<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">comment<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">download_comments<\/span><span class=\"p\">(<\/span><span class=\"n\">youtube_id<\/span><span class=\"p\">):<\/span>\n                <span class=\"n\">sys<\/span><span class=\"p\">.<\/span><span class=\"n\">stdout<\/span><span class=\"p\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">json<\/span><span class=\"p\">.<\/span><span class=\"n\">dumps<\/span><span class=\"p\">(<\/span><span class=\"n\">comment<\/span><span class=\"p\">,<\/span> <span class=\"n\">ensure_ascii<\/span><span class=\"o\">=<\/span><span class=\"bp\">False<\/span><span class=\"p\">))<\/span>\n                <span class=\"n\">count<\/span> <span class=\"o\">+=<\/span> <span class=\"mi\">1<\/span>\n                <span class=\"n\">sys<\/span><span class=\"p\">.<\/span><span class=\"n\">stdout<\/span><span class=\"p\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s\">'Downloaded %d comment(s)<\/span><span class=\"se\">\\r<\/span><span class=\"s\">'<\/span> <span class=\"o\">%<\/span> <span class=\"n\">count<\/span><span class=\"p\">)<\/span>\n                <span class=\"n\">sys<\/span><span class=\"p\">.<\/span><span class=\"n\">stdout<\/span><span class=\"p\">.<\/span><span class=\"n\">flush<\/span><span class=\"p\">()<\/span>\n                <span class=\"k\">if<\/span> <span class=\"n\">limit<\/span> <span class=\"ow\">and<\/span> <span class=\"n\">count<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"n\">limit<\/span><span class=\"p\">:<\/span>\n                    <span class=\"k\">break<\/span>\n        <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'<\/span><span class=\"se\">\\n<\/span><span class=\"s\">Done!'<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">except<\/span> <span class=\"nb\">Exception<\/span> <span class=\"k\">as<\/span> <span class=\"n\">e<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'Error:'<\/span><span class=\"p\">,<\/span> <span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">e<\/span><span class=\"p\">))<\/span>\n        <span class=\"n\">sys<\/span><span class=\"p\">.<\/span><span class=\"nb\">exit<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"saving-comments-for-parsed-video-ids\">Saving comments for parsed video ids<\/h3>\n\n<p>The code above has some magic numbers, like limit=100 default parameter in comments_main \u2013 consciously not so big as the crawler is illustrating a collection of a small scientific corpus. \nCollecting comments in a loop:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">wdir<\/span> <span class=\"o\">=<\/span> <span class=\"sa\">r<\/span><span class=\"s\">'\/media\/user\/youtube\/comments'<\/span>\n<span class=\"n\">uids<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"sa\">r<\/span><span class=\"s\">'\/media\/user\/youtube\/'<\/span><span class=\"o\">+<\/span><span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">datetime<\/span><span class=\"p\">.<\/span><span class=\"n\">date<\/span><span class=\"p\">.<\/span><span class=\"n\">today<\/span><span class=\"p\">())<\/span><span class=\"o\">+<\/span><span class=\"s\">'.txt'<\/span><span class=\"p\">,<\/span> <span class=\"s\">'r'<\/span><span class=\"p\">,<\/span> <span class=\"n\">encoding<\/span><span class=\"o\">=<\/span><span class=\"s\">'utf8'<\/span><span class=\"p\">).<\/span><span class=\"n\">readlines<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">uids<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">.<\/span><span class=\"n\">strip<\/span><span class=\"p\">(<\/span><span class=\"s\">'\/watch?v='<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">uids<\/span><span class=\"p\">]<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">tqdm<\/span><span class=\"p\">(<\/span><span class=\"n\">uids<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">uid<\/span> <span class=\"o\">=<\/span> <span class=\"n\">i<\/span><span class=\"p\">.<\/span><span class=\"n\">strip<\/span><span class=\"p\">(<\/span><span class=\"s\">'<\/span><span class=\"se\">\\n<\/span><span class=\"s\">'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">output<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"p\">.<\/span><span class=\"n\">path<\/span><span class=\"p\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">wdir<\/span><span class=\"p\">,<\/span> <span class=\"n\">uid<\/span><span class=\"o\">+<\/span><span class=\"s\">'_'<\/span><span class=\"o\">+<\/span><span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">datetime<\/span><span class=\"p\">.<\/span><span class=\"n\">date<\/span><span class=\"p\">.<\/span><span class=\"n\">today<\/span><span class=\"p\">())<\/span><span class=\"o\">+<\/span><span class=\"s\">'.txt'<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">comments_main<\/span><span class=\"p\">(<\/span><span class=\"n\">uid<\/span><span class=\"p\">,<\/span> <span class=\"n\">output<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"power-up\">Power-up!<\/h3>\n\n<p>Finally, what you can (optionally) do is<\/p>\n<ul>\n  <li><a href=\"https:\/\/www.adminschoice.com\/crontab-quick-reference\">configure a crontab<\/a> task for your script<\/li>\n  <li>make a proxy cycle with <a href=\"https:\/\/www.scrapehero.com\/how-to-rotate-proxies-and-ip-addresses-using-python-3\/\">Round Robin<\/a><\/li>\n<\/ul>\n\n<h4 id=\"thats-it-you-are-awesome\">That\u2019s it! You are awesome!<\/h4>\n\n<p><img src=\"https:\/\/img5.goodfon.ru\/wallpaper\/nbig\/f\/24\/ochki-kianu-rivz-protez-keanu-reeves-cyberpunk-2077-cd-proje.jpg\" alt=\"\" \/><\/p>\n\n<p>Full Jupyter Notebook can be found <a href=\"https:\/\/github.com\/TatianaShavrina\/crawlers\/blob\/master\/youtube_crawl_demo.ipynb\">here<\/a> (messy code)<\/p>","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"category":[{"@attributes":{"term":"NLP"}},{"@attributes":{"term":"crawling"}},{"@attributes":{"term":"youtube"}},{"@attributes":{"term":"corpus"}},{"@attributes":{"term":"social media"}},{"@attributes":{"term":"python"}}],"summary":"All ways to do it"},{"title":"NLP Highlights at NeurIPS 2019","link":{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/2019\/12\/27\/neurips\/","rel":"alternate","type":"text\/html","title":"NLP Highlights at NeurIPS 2019"}},"published":"2019-12-27T00:00:00+00:00","updated":"2019-12-27T00:00:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/2019\/12\/27\/neurips","content":"<p>NeurIPS conference is usually less populated by NLP people \u00af_(\u30c4)_\/\u00af<\/p>\n\n<p>But since some of us, including me, happened to get there in 2019, I want to make a review post and highlight the main works that were devoted specifically to working with the natural language.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/3648\/1*wPImlEXRHrX9AqH3OZRLhg.png\" alt=\"\" \/><\/p>\n\n<p>Main NLP works can be divided into the following categories:<\/p>\n<ol>\n  <li><a href=\"#emb\">Everything with Embeddings<\/a><\/li>\n  <li><a href=\"#brain\">Reasoning and Modelling Brain<\/a><\/li>\n  <li><a href=\"#new\">New Architectures<\/a><\/li>\n  <li><a href=\"#finally\">Finally Officially Released<\/a><\/li>\n  <li><a href=\"#demo\">Demos<\/a><\/li>\n<\/ol>\n\n<p><em>Yes, the categorization is quite <a href=\"https:\/\/en.wikipedia.org\/wiki\/Celestial_Emporium_of_Benevolent_Knowledge\">Borgesian<\/a>, who cares, let\u2019s dive in!<\/em><\/p>\n\n<div id=\"emb\" \/>\n\n<h2 id=\"everything-with-embeddings\">Everything with Embeddings<\/h2>\n\n<h3 id=\"on-the-downstream-performance-of-compressed-word-embeddings\">On the Downstream Performance of Compressed Word Embeddings<\/h3>\n\n<p><a href=\"https:\/\/nips.cc\/Conferences\/2019\/Schedule?showEvent=14602\">link<\/a><\/p>\n\n<p>The paper presents an approach similar to the direction of the distillation of models, in particular, BERT. Researchers at Stanford solved the same problem as everyone else \u2014 although embeddings are extremely effective, they demand a lot of memory and work slowly.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1207\/1*BsObmHClgQ7Pgcd1MR6UZA.png\" alt=\"\" \/><\/p>\n\n<p>The idea is not to learn a smaller model but to find new ways to effectively compress the weights of ready-made models, and develop metrics that would show how well the compression went.<\/p>\n\n<p>Models in concern \u2014 word2vec, fasttext \u0438 BERT, for the last all word vectors were taken from the vocabulary, being non-contextual.<\/p>\n\n<p>Compression model: SVD + Eigenspace Overlap Score (EOS)<\/p>\n\n<p>A correlation of word vectors in a bigger model and in a compressed one is proposed as a metric of compression quality. The results show that the proposed compression method allows reproducing 91% of the results for Fasttext and 92% of the BERT results.<\/p>\n\n<h3 id=\"spherical-text-embedding\">Spherical Text Embedding<\/h3>\n\n<p><a href=\"https:\/\/nips.cc\/Conferences\/2019\/Schedule?showEvent=13890\">link<\/a><\/p>\n\n<p>Text embeddings are widely used and show excellent results on many NLP tasks. However, they are usually trained within the Euclidean space, while their use is usually the calculation of cosine proximity, clustering (that is, typical application tasks: word similarity and document clustering). That is, the training phase and the use phase occur as if in different planes, while if you teach spherical embeddings right away, then the quality should become better.<\/p>\n\n<p>As a result, a spherical generative model is proposed, where the word level and paragraph level embeddings are trained together. To train text embeddings in a spherical space, the authors developed an effective convergence optimization algorithm based on Riemannian optimization. The resulting model is highly efficient and achieves high results and even beats BERT somewhere (for example, just in the task of clustering documents).<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1194\/0*ZY9K3WRi0Od2kzvZ\" alt=\"\" \/><\/p>\n\n<h3 id=\"embedding-symbolic-knowledge-into-deep-networks\">Embedding Symbolic Knowledge into Deep Networks<\/h3>\n\n<p><a href=\"https:\/\/neurips.cc\/Conferences\/2019\/Schedule?showEvent=13580\">link<\/a><\/p>\n\n<p>The authors proposed a network of logical embeddings with semantic regularization (LENSR), which encodes previous knowledge at the symbol level, which allows improving the quality and performance of deep models.\nLENSR is a graph embedding network that projects propositional formulas onto a distribution through an extended graph convolution network (GCN). To create semantically correct embeddings, the authors developed methods for recognizing node heterogeneity and semantic regularization, which adds structural constraints to embeddings.<\/p>\n\n<p>The authors compared the quality of the various embeddings of logical representations:\n1) General form\n2) Conjunctive Normal Form (CNF).\n3) Deterministic Decomposable Negation Normal Form (d-DNNF)<\/p>\n\n<p>LENSR is effective for entailment (synthetic dataset) and visual relationship prediction (VRD dataset) tasks.<\/p>\n\n<p>Interestingly, there is a connection between the theory of propositions and the simplification of embeddings. Further study of this relationship may clarify the relationship between compiling knowledge and teaching vector representation.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/904\/1*Kc8AbasSiSB-IuygOGGotQ.jpeg\" alt=\"\" \/><\/p>\n\n<div id=\"brain\" \/>\n\n<h2 id=\"reasoning-and-modelling-brain\">Reasoning and Modelling Brain<\/h2>\n\n<h3 id=\"interpreting-and-improving-natural-language-processing-in-machines-with-natural-language-processing-in-the-brain\">Interpreting and improving natural-language processing (in machines) with natural language-processing (in the brain)<\/h3>\n\n<p><a href=\"https:\/\/nips.cc\/Conferences\/2019\/Schedule?showEvent=14403\">link<\/a><\/p>\n\n<p>Interesting interdisciplinary work at the junction of neuroscience and NLP (all about understanding how the brain works, you can better understand what happens in artificial networks).\nThe authors proposed the following approach \u2014 they used recordings of the brain activity of subjects who read a narrative text, and based on this interpreted various representations obtained from neural networks such as BERT, XLNet.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1560\/0*YdVqbSo3NThB5aBu\" alt=\"\" \/><\/p>\n\n<p>Some results:<\/p>\n<ul>\n  <li>Transformer models (BERT and Transformer-XL) capture the most important contextual information for the brain in their middle layers of the network<\/li>\n  <li>Transformer-XL combines both the properties of the transformer and the recurrence properties, which prevents loss of quality even in very long contexts, in contrast to purely recurrent models or purely transformers.<\/li>\n  <li>Consistent focus on shallow layers in BERT actually improves both brain predictions and NLP syntax performance<\/li>\n<\/ul>\n\n<p>Next steps: to get more informative conceptual signs from brain MRI, which would contain specific conceptual information and understanding which area of the brain is responsible for what; use this further to study how semantic information is represented in networks.<\/p>\n\n<h3 id=\"quantum-embedding-of-knowledge-for-reasoning\">Quantum Embedding of Knowledge for Reasoning<\/h3>\n\n<p><a href=\"https:\/\/neurips.cc\/Conferences\/2019\/Schedule?showEvent=13687\">link<\/a><\/p>\n\n<p>A qualitatively new approach to the transfer of symbolic knowledge bases into a vector space that uses quantum logic (Birkhoff and von Neumann\u2019s concept of 1936). Each type of relationship becomes its own axis in this space. Complications arise with logical relationships (conjunction and disjunction operators), but the authors of the article managed to show their implementation.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/436\/0*OH93nZnUR7YAQxas\" alt=\"\" \/><\/p>\n\n<p>The result of application for the communication prediction problem is (Hit @ 1 = 0.96) (previous record 0.74). Unfortunately, there is a negative side to the question: if the previous algorithm, even if it did not get the first result, it was highly likely to hit the top ten \u2014 Hit@10=0.89. Quantum embeddings do not have this property: if the correct answer is not the first, then the probability of getting into the top ten is zero.<\/p>\n\n<h3 id=\"learning-by-abstraction-the-neural-state-machine\">Learning by Abstraction: The Neural State Machine<\/h3>\n\n<p><a href=\"https:\/\/papers.nips.cc\/paper\/8825-learning-by-abstraction-the-neural-state-machine\">link<\/a><\/p>\n\n<p>The work is devoted to the creation of a finite state machine that would connect computer vision and text processing, but not of the ordinary text of a natural language, but of the \u201cmental language\u201d \u2014 an unambiguous language for describing objects and thinking (fictional abstraction).<\/p>\n\n<p>The idea is dictated by the works of the largest philosophers and theorists of the language \u2014 Chomsky, Wittgenstein. But the implementation is extremely superficial \u2014 de facto simply sets of attributes of each item from the dictionary are taken:<\/p>\n<ol>\n  <li>Item \u2014 Color\n    <ul>\n      <li>Item \u2014 Size<\/li>\n      <li>Item \u2014 Form<\/li>\n    <\/ul>\n  <\/li>\n<\/ol>\n\n<p>As well as signs characteristic of certain types of objects \u2014 poses, conditions, etc.<\/p>\n\n<p>We then use these natural attributes of objects to build from them the embedding of the object. We build embedding based on feature vectors, and not on contexts in the corpus of texts.<\/p>\n\n<p>Further, all this is transferred to the architecture of the neural network finite state machine with attention, which receives recognized objects from the picture and the question and learns to predict the answer.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/350\/1*ARjkfr9h1kt32EtMo5_qvw.png\" alt=\"\" \/><\/p>\n\n<p>As a result, we get the ability to answer tricky questions about what is happening in the pictures. + SOTA increase from 3 to 10% on various test datasets.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1166\/1*U1MGgY7ivqiWISAxYRkftw.png\" alt=\"\" \/><\/p>\n\n<div id=\"new\" \/>\n\n<h2 id=\"new-architectures\">New Architectures<\/h2>\n\n<h3 id=\"levenshtein-transformer\">Levenshtein Transformer<\/h3>\n\n<p><a href=\"https:\/\/nips.cc\/Conferences\/2019\/Schedule?showEvent=14112\">link<\/a><\/p>\n\n<p>A new attempt at transformers based on classic edit-distance steps. The attention block was removed from the transformer and replaced with the Levenshtein block (actions: replace \/ delete \/ insert)<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1082\/1*WhwKOoWgnD1IfaayDu1u_A.jpeg\" alt=\"\" \/><\/p>\n\n<h3 id=\"ordered-memory\">Ordered Memory<\/h3>\n\n<p><a href=\"https:\/\/github.com\/yikangshen\/Ordered-Memory\/blob\/master\/Ordered_Memory_Slides.pdf\">link<\/a><\/p>\n\n<p>Microsoft\u2019s new neuron architecture, which is likely to replace the most popular recurrent architectures.<\/p>\n\n<p>Motivation \u2014 Recent research shows that recursive, consistent memorization of characters and words in a text is critical to the generalizing ability of AI.<\/p>\n\n<p>Moreover, in many languages \u200b\u200b(including English and Russian), you need to remember a fairly large context, since the syntax tree in these languages \u200b\u200bcan have connected words very far from each other.<\/p>\n\n<p>The new architecture handles long contexts and graph structures much better than LSTM, including through the attention mechanism and sequentially weighting each new character \/ word in a sequence.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/986\/1*tu10PsbFBu3XHy3h3-qNRQ.png\" alt=\"\" \/><\/p>\n\n<p>On existing datasets on which you want to restore information about a language sequence or tree, the architecture has achieved significant accuracy<\/p>\n<ul>\n  <li>ListOps \u2014 99.7% accuracy<\/li>\n<\/ul>\n\n<h3 id=\"hierarchical-decision-making-by-generating-and-following-natural-language-instructions\">Hierarchical Decision Making by Generating and Following Natural Language Instructions<\/h3>\n\n<p><a href=\"https:\/\/neurips.cc\/Conferences\/2019\/Schedule?showEvent=14024\">link<\/a><\/p>\n\n<p>The work is dedicated to teaching the best strategies in an online game based on the instructions that users give each other.<\/p>\n\n<p>The data from the interactions of user pairs is used:<\/p>\n<ul>\n  <li>Strategist \u2014 decides what action to take immediately: put the peasants on the field, build a castle, start a war with a distant castle, etc.<\/li>\n  <li>Executor \u2014 receives a command from a strategist and performs actions that he considers necessary, including following instructions.<\/li>\n<\/ul>\n\n<p>In an ideal situation for training a neural network, the executor performs only those actions that the strategist describes to him, although in reality this, of course, is not entirely true \u2014 the executor skips some commands and does a lot \u201con his own.\u201d<\/p>\n\n<p>Based on the statement (erroneous!) That the language is compositional, and the meaning of the team is made up of the meanings of individual words in the sentence, the researchers built an architecture that connects user actions on the field and messages received from the strategist.<\/p>\n\n<p>The resulting architecture includes two subnets training together:<\/p>\n<ul>\n  <li>Instructor \u2014 RNN Generative, accepts the screen state as an input, generates instructions \u2014 what to do next<\/li>\n  <li>Executor \u2014 LSTM, accepts the screen state, instructions, history of previous states and instructions as input \u2014 selects the action that needs to be done.<\/li>\n<\/ul>\n\n<p>As a result of the use of such a Joint architecture, the win rate and the ability to make both long-term and short-term decisions in strategic games (due to the memory of recurrent networks) are significantly increased.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/998\/1*H-SnViRz4f0cwY8mXSiMXA.png\" alt=\"\" \/><\/p>\n\n<div id=\"finally\" \/>\n\n<h2 id=\"finally-officially-released\">Finally Officially Released<\/h2>\n\n<h3 id=\"superglue-a-stickier-benchmark-for-general-purpose-language-understanding-systems\">SuperGLUE: A Stickier Benchmark for General-Purpose Language Understanding Systems<\/h3>\n\n<p><a href=\"https:\/\/super.gluebenchmark.com\/\">link<\/a><\/p>\n\n<p>In recent years, many new pre-trained models have appeared, transfer lening shows amazing results on various NLU tasks, but there is no unambiguous reliable assessment of these models.<\/p>\n\n<p>The GLUE benchmark presented a little over a year ago, offers a unique metric, which is an average score for 9 different classification problems (tonality analysis, paraphrase, etc.). However, recently the test assessment has exceeded the level of expert evaluation of people, which limits further research.<\/p>\n\n<p>In the article, the authors present the SuperGLUE metric, a new benchmark developed in the GLUE style.<\/p>\n\n<p>SuperGLUE Tasks:<\/p>\n<ul>\n  <li>standardize assessment<\/li>\n  <li>provide an unambiguous metric for NLU models<\/li>\n<\/ul>\n\n<p>SuperGLUE \u2014 averaged evaluation on 8 NLU tasks:<\/p>\n<ul>\n  <li>tasks are more diverse (for example, coreference, QA, etc.).<\/li>\n  <li>selected tasks are hard to evaluate, hard to evaluate for machines, easy for humans<\/li>\n  <li>selected tasks with a small data set for training<\/li>\n<\/ul>\n\n<p>For SuperGLUE also available:\n\u25cb additional diagnostics\n\u25cb updated rules\n\u25cb start code<\/p>\n\n<p>SuperGLUE is available at https:\/\/super.gluebenchmark.com and provides an open leaderboard for comparing models.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1600\/0*gx8fHukTuFPEPajf\" alt=\"\" \/><\/p>\n\n<h3 id=\"xlnet-generalized-autoregressive-pretraining-for-language-understanding\">XLNet: Generalized Autoregressive Pretraining for Language Understanding<\/h3>\n\n<p><a href=\"https:\/\/nips.cc\/Conferences\/2019\/Schedule?showEvent=13223\">link<\/a><\/p>\n\n<p>Modern universal transformers have their pros and cons:\nAuto-regressive models (ELMo, GPT)<\/p>\n<ul>\n  <li>Free of artificial noise in the data<\/li>\n  <li>No bidirectional context<\/li>\n<\/ul>\n\n<p>Denoising auto-encoding (BERT)<\/p>\n<ul>\n  <li>Can make independent predictions<\/li>\n  <li>Trained on noisy data with {mask}<\/li>\n  <li>Have a natural context on the right and left<\/li>\n<\/ul>\n\n<p>How to combine the advantages of both approaches?\nXLNET architecture \u2014 Permutation Language Modeling (PLM), combining autoregressive + bidirectional approaches<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/910\/1*MbLBKX6iHFo6yTznt-38cg.png\" alt=\"\" \/><\/p>\n\n<p>(we use all permutations of words in teaching a language model)\nTransformer XL built on segment-level recurrence + relative positional attention<\/p>\n\n<div id=\"demo\" \/>\n\n<h2 id=\"demos\">Demos<\/h2>\n\n<h3 id=\"allennlp-interpret-explaining-predictions-of-nlp-models\">AllenNLP Interpret: Explaining Predictions of NLP Models<\/h3>\n\n<p><a href=\"https:\/\/allennlp.org\/interpret\">link<\/a><\/p>\n\n<p>Combining Transformer XL with PLM gives SOTA a GLUE result compared to other models, in particular BERT and RoBERTa.<\/p>\n\n<p>The AllenNLP introduces a new tool for interactive interpretations of various NLP models.<\/p>\n\n<p>This tool makes it easy to apply gradient significance maps and competitive attacks to new models, as well as develop new interpretation methods.\nThe AllenNLP interpretation contains three components:<\/p>\n<ul>\n  <li>set of interpretation methods applicable to most models<\/li>\n  <li>API for developing new interpretation methods (for example, API for receiving input gradients)<\/li>\n  <li>reusable web components to visualize results<\/li>\n<\/ul>\n\n<p>Models available for interpretation: masking language models, sentiment analysis, SQuAD, Textual Entailment, NER and many others.<\/p>\n\n<p>The system also allows you to upload your own models to pytorch in the same format and receive analysis and visualization.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1600\/0*oKNliVhMg3Uuyh96\" alt=\"\" \/><\/p>\n\n<h3 id=\"exbert-a-visual-analysis-tool-to-explain-berts-learned-representations\">exBERT: A Visual Analysis Tool to Explain BERT\u2019s Learned Representations<\/h3>\n\n<p><a href=\"http:\/\/exbert.net\/\">link<\/a><\/p>\n\n<p>Language models are a very powerful tool in NLP tasks that provides a contextual representation of a language. However, it is often difficult to understand what exactly the attention mechanism has learned, what specific information is encoded.<\/p>\n\n<p>ExBERT is an interactive tool introduced by IBM that allows users to explore what and how the transformer learned in the process of creating a language model.<\/p>\n\n<p>The tool works with the pre-trained base version of Bert. To understand what your architecture has learned, you need to submit a natural language offer as input, exBert will parse the offer into tokens suitable for BERT (BPE tokenizer) and feed the tokens into the model. Then the attenuations and subsequent embeddings for each encoder are extracted and displayed for interactive work with them.\nexBERT focuses specifically on self-attention, that is, the mechanism of attention of words in a sentence relative to other words in the same sentence.<\/p>\n\n<p>To facilitate the interpretation of language parameters, several key BERT functions were disabled (masking was saved):<\/p>\n<ul>\n  <li>attention for [CLS] and [SEP] disabled<\/li>\n  <li>there is no functionality for exploring attention between different words<\/li>\n<\/ul>\n\n<p>The authors advise working with the tool within a single sentence, despite the fact that BERT is able to analyze large pieces of a paragraph immediately.<\/p>\n\n<p><img src=\"https:\/\/miro.medium.com\/max\/1600\/0*mm47rSO7oq09nE5w\" alt=\"\" \/><\/p>\n\n<h2 id=\"instead-of-an-epilogue\">Instead of an epilogue<\/h2>\n\n<p>Although NLP is not a very specialized topic at NeurIPS, it follows the same trends as AI in general. Join us in NLP, we have<\/p>\n<ul>\n  <li>interpretation problems<\/li>\n  <li>general models and their distillation<\/li>\n  <li>and a big dream of recreating consciousness.<\/li>\n<\/ul>\n\n<p>I will finally include here a keynote by Yoshua Bengio as being absolutely brilliant in blurring the boundaries between the technical component of machine learning and the theory of reasoning, capturing causality and obtaining systematic generalization in natural language processing.<\/p>\n\n<p>And thanks for reading!<\/p>\n\n<p><a href=\"https:\/\/youtu.be\/FtUbMG3rlFs\"><img src=\"https:\/\/img.youtube.com\/vi\/FtUbMG3rlFs\/0.jpg\" alt=\"IMAGE ALT TEXT HERE\" \/><\/a><\/p>","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"category":[{"@attributes":{"term":"NLP"}},{"@attributes":{"term":"neurips"}},{"@attributes":{"term":"deep learning"}},{"@attributes":{"term":"NeurIPS2019"}}],"summary":"NeurIPS conference is usually less populated by NLP people \u00af_(\u30c4)_\/\u00af"},{"title":"An exhaustive list of open-source corpora for Russian","link":{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/datasets\/","rel":"alternate","type":"text\/html","title":"An exhaustive list of open-source corpora for Russian"}},"published":"2018-08-30T00:00:00+00:00","updated":"2018-08-30T00:00:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/datasets","content":"<p>During my work as an NLP-engineer, I always encountered a lot of corpus projects, that are not so publicly well-known and mentioned, yet they are a good source of text data for different kinds of research.\nHere I share this list with you, not forgetting to include more popular projects in it, of course, so that the list was complete.<\/p>\n\n<p>Send your pull-requests <a href=\"https:\/\/github.com\/TatianaShavrina\/blog\/blob\/master\/_posts\/2018-08-30-datasets.md\">to this post<\/a> or comment below, if you know any dataset or corpus of the Russian language, which is not mentioned here!<\/p>\n\n<h1 id=\"table-of-contents\">Table of contents<\/h1>\n<ol>\n  <li><a href=\"#big\">Big and Open<\/a><\/li>\n  <li><a href=\"#special\">Special Corpora<\/a>\n    <ol>\n      <li><a href=\"#morpho\">Morphology and Syntax<\/a><\/li>\n      <li><a href=\"#ner\">NER Parsing<\/a><\/li>\n      <li><a href=\"#spell\">Spellcheckers<\/a><\/li>\n      <li><a href=\"#sent\">Sentiment Analysis<\/a><\/li>\n    <\/ol>\n  <\/li>\n  <li><a href=\"#deriv\">Open Corpus Derivatives<\/a>\n    <ol>\n      <li><a href=\"#vector\">Vector Models<\/a><\/li>\n      <li><a href=\"#ngram\">N-grams<\/a><\/li>\n    <\/ol>\n  <\/li>\n<\/ol>\n\n<p><img src=\"https:\/\/img.scoop.it\/SsfrZojfM8hw4ckjn4K_EDl72eJkfbmt4t8yenImKBVvK0kTmF0xjctABnaLJIm9\" alt=\"\" \/><\/p>\n\n<div id=\"big\" \/>\n\n<h1 id=\"big-and-open\">Big and Open<\/h1>\n\n<ul>\n  <li><strong>Russian Twitter Corpus<\/strong><\/li>\n<\/ul>\n\n<p>The corpus of short texts in Russian on the basis of  Twitter posts. \nSuitable for training a language model for social media or for short texts, training classifiers for sentiment analysis and text toxicity.<\/p>\n\n<p>17,6 million tweets available!<\/p>\n\n<p><a href=\"http:\/\/study.mokoron.com\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Russian Common Crawl Data<\/strong><\/li>\n<\/ul>\n\n<p>541 TB of raw text data from the web. Contains duplicates, sources and dates of the web-pages are non-obvious.<\/p>\n\n<p><a href=\"https:\/\/wwwdb.inf.tu-dresden.de\/misc\/dwtc\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Taiga Corpus<\/strong><\/li>\n<\/ul>\n\n<p><em>minute of shameless self-promotion<\/em><\/p>\n\n<p>Taiga corpus is a corpus project to become the largest fully available webcorpus constructed from open text sources. Data available on request, containing datasets for text classification, language modelling, fake news detection, thematic modelling, authorship attribution, social media research, etc.<\/p>\n\n<p>6,5 billion of tokens available<\/p>\n\n<p><a href=\"https:\/\/github.com\/TatianaShavrina\/taiga_site\">link<\/a><\/p>\n\n<hr \/>\n\n<div id=\"special\" \/>\n\n<h1 id=\"special-corpora\">Special Corpora<\/h1>\n\n<div id=\"morpho\" \/>\n\n<h2 id=\"morphology-and-syntax\">Morphology and Syntax<\/h2>\n\n<ul>\n  <li><strong>OpenCorpora<\/strong><\/li>\n<\/ul>\n\n<p>The first open-source corpus for Russian - about 2 million words in manual annotation available + dictionary<\/p>\n\n<p><a href=\"https:\/\/nlpub.ru\/OpenCorpora\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Russian National Corpus<\/strong><\/li>\n<\/ul>\n\n<p>A subcorpus of Russian National Corpus is distributed openly by request.\nMorphological annotation with manual verification.<\/p>\n\n<p>1 million words available<\/p>\n\n<p><a href=\"http:\/\/ruscorpora.ru\/corpora-usage.html\">link<\/a><\/p>\n\n<ul>\n  <li><strong>General Internet-Corpus of Russian<\/strong><\/li>\n<\/ul>\n\n<p>LiveJournal and VKontakte corpus with the automatically resolved ambiguity<\/p>\n<ul>\n  <li>2 million wordforms available<\/li>\n<\/ul>\n\n<p>Annotation: Abbyy Compreno + rule-based verification\nAvailable by request<\/p>\n\n<p><a href=\"http:\/\/www.webcorpora.ru\/silver\">link<\/a><\/p>\n\n<ul>\n  <li><strong>MorphoRuEval Data<\/strong><\/li>\n<\/ul>\n\n<p>Data for MorphoRuEval track - competition of automatic POS-tagging for Russian<\/p>\n\n<p>Consists of:<\/p>\n<ul>\n  <li>plain texts:\n1) LiveJournal (from GICR) 30 million words\n2) Facebook, Twitter, VKontakte\u201430 million words\n3) Librusec\u2014300 million words<\/li>\n  <li>annotated data:\n1) RNC Open: a manually disambiguated subcorpus of the Russian National Corpus\u20141.2 million words (fiction, news, nonfiction, spoken, blog)\n2) GICR corpus with the resolved homonymy\u20141 million words\n3) OpenCorpora.org data\u2014400 thousand tokens\n4) UD SynTagRus\u2014900 thousand tokens (fiction, news)<\/li>\n<\/ul>\n\n<p><a href=\"https:\/\/github.com\/dialogue-evaluation\/morphoRuEval-2017\">link<\/a><\/p>\n\n<ul>\n  <li><strong>SynTagRus<\/strong><\/li>\n<\/ul>\n\n<p>A subcorpus of Russian National Corpus, with fiction and news, with manual syntactic annotation.\nNow converted to UD.<\/p>\n<ul>\n  <li>1 million sentences available<\/li>\n<\/ul>\n\n<p><a href=\"https:\/\/github.com\/UniversalDependencies\/UD_Russian-SynTagRus\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Russian Universal Dependencies Data<\/strong><\/li>\n<\/ul>\n\n<p>Russian texts with morphological and syntactic annotation in UD, checked manually<\/p>\n\n<p><a href=\"https:\/\/github.com\/UniversalDependencies\/UD_Russian-SynTagRus\">SynTagRus, one more time<\/a><\/p>\n\n<p><a href=\"http:\/\/universaldependencies.org\/treebanks\/ru_gsd\/index.html\">GSD<\/a><\/p>\n\n<p><a href=\"http:\/\/universaldependencies.org\/treebanks\/ru_pud\/index.html\">Parallel Universal Dependencies (PUD)<\/a><\/p>\n\n<p><a href=\"http:\/\/universaldependencies.org\/treebanks\/ru_taiga\/index.html\">Taiga<\/a><\/p>\n\n<div id=\"ner\" \/>\n\n<h2 id=\"ner-parsing\">NER Parsing<\/h2>\n\n<ul>\n  <li><strong>FactRuEval Data<\/strong><\/li>\n<\/ul>\n\n<p>Data from FactRuEval track for Russian - ORG, LOC, PER, LOCORG annotation with manual verification - from Lentapedia project and Wikinews<\/p>\n\n<p><a href=\"https:\/\/github.com\/dialogue-evaluation\/factRuEval-2016\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Gareev Corpus<\/strong><\/li>\n<\/ul>\n\n<p>A corpus compiled for different sources, training set for <a href=\"https:\/\/github.com\/deepmipt\/ner\">Deepmipt Ner system<\/a><\/p>\n\n<p><a href=\"https:\/\/link.springer.com\/chapter\/10.1007\/978-3-642-37247-6_27\">link<\/a><\/p>\n\n<p>Available by request, see paper:<\/p>\n\n<p>Rinat Gareev, Maksim Tkachenko, Valery Solovyev, Andrey Simanovsky, Vladimir Ivanov: Introducing Baselines for Russian Named Entity Recognition. Computational Linguistics and Intelligent Text Processing, 329 \u2013 342 (2013).<\/p>\n\n<ul>\n  <li><strong>Persons-1000<\/strong><\/li>\n<\/ul>\n\n<p>The collection of NER-tagged sentences annotated by experts of the Russian Academy of Sciences<\/p>\n\n<p><a href=\"http:\/\/labinform.ru\/pub\/named_entities\/descr_ne.htm\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Wikipedia Dumps<\/strong><\/li>\n<\/ul>\n\n<p>Russian Wikipedia - full articles in different formats<\/p>\n<ul>\n  <li>300 millions of tokens available<\/li>\n<\/ul>\n\n<p><a href=\"http:\/\/linguatools.org\/tools\/corpora\/wikipedia-monolingual-corpora\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>DBpedia<\/strong><\/li>\n<\/ul>\n\n<p>Structured Info from Wikipedia in many languages, including Russian<\/p>\n\n<p><a href=\"https:\/\/wiki.dbpedia.org\/develop\/datasets\/dbpedia-version-2016-10\">link<\/a><\/p>\n\n<p>RDF format downloads:<\/p>\n\n<p><a href=\"https:\/\/wiki.dbpedia.org\/downloads-2016-10#p10608-2\">link<\/a><\/p>\n\n<div id=\"spell\" \/>\n\n<h2 id=\"spellcheckers\">Spellcheckers<\/h2>\n\n<ul>\n  <li><strong>SpellRuEval trainset<\/strong><\/li>\n<\/ul>\n\n<p>Dataset for SpellRuEval task for Russian - a track for spell-checkers for social media<\/p>\n\n<p>10 000 sentences with errors from General Internet-Corpus of Russian<\/p>\n\n<p><a href=\"http:\/\/www.dialog-21.ru\/evaluation\/2016\/spelling_correction\/\">link<\/a><\/p>\n\n<div id=\"sent\" \/>\n\n<h2 id=\"sentiment-analysis\">Sentiment Analysis<\/h2>\n\n<ul>\n  <li><strong>Russian Twitter Corpus<\/strong> <em>(again)<\/em><\/li>\n<\/ul>\n\n<p>The corpus of short texts in Russian on the basis of  Twitter posts. \nSuitable for training a language model for social media or for short texts, training classifiers for sentiment analysis and text toxicity.<\/p>\n\n<p>17,6 million tweets available for downloading!<\/p>\n\n<p><a href=\"http:\/\/study.mokoron.com\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>SentiRuEval trainset<\/strong><\/li>\n<\/ul>\n\n<p>Dataset from SentiRuEval task, 2016<\/p>\n\n<p>about 20 000 tagged Tweets with manual verification<\/p>\n\n<p><a href=\"https:\/\/drive.google.com\/drive\/u\/0\/folders\/0BxlA8wH3PTUfV1F1UTBwVTJPd3c\">link<\/a><\/p>\n\n<hr \/>\n\n<div id=\"deriv\" \/>\n\n<h1 id=\"open-corpus-derivatives\">Open Corpus Derivatives<\/h1>\n\n<div id=\"vector\" \/>\n\n<h2 id=\"vector-models\">Vector Models<\/h2>\n\n<ul>\n  <li><strong>RusVectores Vector Models<\/strong><\/li>\n<\/ul>\n\n<p>On RusVectores project you can download pre-trained fasttext, word2vec and doc2vec models on the main Russian webcorpora:\n(all available under CC licence)<\/p>\n\n<ul>\n  <li>Russian National Corpus<\/li>\n  <li>Taiga<\/li>\n  <li>General Internet-Corpus<\/li>\n  <li>Aranea<\/li>\n  <li>News Corpus<\/li>\n  <li>Wikipedia<\/li>\n<\/ul>\n\n<p><a href=\"http:\/\/rusvectores.org\/ru\/models\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Word2vec<\/strong><\/li>\n<\/ul>\n\n<p>Russian skip-gram model available for download resulting from the RUSSE evaluation track<\/p>\n\n<p><a href=\"https:\/\/github.com\/nlpub\/russe-evaluation\/tree\/master\/russe\/measures\/word2vec\">link<\/a><\/p>\n\n<div id=\"ngram\" \/>\n\n<h2 id=\"ngrams\">Ngrams<\/h2>\n\n<ul>\n  <li><strong>Google Ngrams<\/strong><\/li>\n<\/ul>\n\n<p>N-grams, calculated on Google Books data - available for multiple languages, Russian as well<\/p>\n\n<p><a href=\"http:\/\/storage.googleapis.com\/books\/ngrams\/books\/datasetsv2.html\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Russian National Corpus Ngrams<\/strong><\/li>\n<\/ul>\n\n<p>N-grams on Russian National Corpus, easy for downloading, top-100 also available.<\/p>\n\n<p><a href=\"http:\/\/www.ruscorpora.ru\/corpora-freq.html\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Alexa Ngrams<\/strong><\/li>\n<\/ul>\n\n<p>Data from top 10M domains of the web. More than 5 billion ngrams available on request for multiple languages:<\/p>\n\n<p><a href=\"https:\/\/data.statoperator.com\/\">link<\/a><\/p>\n\n<ul>\n  <li><strong>Common Crawl Ngrams<\/strong><\/li>\n<\/ul>\n\n<p>N-grams on Common Crawl Corpus, resulting from very noisy, yet big data from the web:<\/p>\n\n<p><a href=\"http:\/\/statmt.org\/ngrams\/\">link<\/a><\/p>\n\n<hr \/>\n\n<p>P.S. There is a great resource called <a href=\"https:\/\/nlpub.ru\/%D0%A0%D0%B5%D1%81%D1%83%D1%80%D1%81%D1%8B#.D0.9A.D0.BE.D1.80.D0.BF.D1.83.D1.81_.D1.82.D0.B5.D0.BA.D1.81.D1.82.D0.BE.D0.B2\">NLPub<\/a>, where some of the resources are also listed.<br \/>\nIt is also good to add the other not listed resources there in wiki articles.<\/p>","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"category":[{"@attributes":{"term":"NLP"}},{"@attributes":{"term":"machine learning"}},{"@attributes":{"term":"data"}},{"@attributes":{"term":"open source"}},{"@attributes":{"term":"Russian"}}],"summary":"During my work as an NLP-engineer, I always encountered a lot of corpus projects, that are not so publicly well-known and mentioned, yet they are a good source of text data for different kinds of research. Here I share this list with you, not forgetting to include more popular projects in it, of course, so that the list was complete."},{"title":"Why do my keras text generation results do not reproduce?","link":{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/keras\/","rel":"alternate","type":"text\/html","title":"Why do my keras text generation results do not reproduce?"}},"published":"2018-08-30T00:00:00+00:00","updated":"2018-08-30T00:00:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/keras","content":"<p>Building a simple and nice text generator in Keras is not a difficult task, yet there are a few mistakes in the framework, that prevent you from succeeding.<\/p>\n\n<p>Today we will discuss a most popular example of an LSTM in Python, written by Trung Tran. <a href=\"https:\/\/chunml.github.io\/ChunML.github.io\/project\/Creating-Text-Generator-Using-Recurrent-Neural-Network\/\">In his post<\/a>, he provides a simple architecture of a 2-layered char LSTM, that can learn rather fast and reproduce simple phrases:<\/p>\n\n<blockquote>\n  <p>\u201cAlbus Dumbledore, I should, do you? But he doesn\u2019t want to adding the thing that you are at Hogwarts, so we can run and get more than one else, you see you, Harry.\u201d<\/p>\n<\/blockquote>\n\n<blockquote>\n  <p>\u201cWhat about this thing, you shouldn\u2019t,\u201d Harry said to Ron and Hermione. \u201cI have no furious test,\u201d said Hermione in a small voice.<\/p>\n<\/blockquote>\n\n<blockquote>\n  <p>\u201cWell, you can\u2019t be the baby way?\u201d said Harry. \u201cHe was a great Beater, he didn\u2019t want to ask for more time.\u201d<\/p>\n<\/blockquote>\n\n<p>The main problem with this code is\u2026<strong>the resulting model is not producing its results after saving, the results seem random even after resuming training!<\/strong><\/p>\n\n<p><img src=\"https:\/\/d3ebicv0uqgr7t.cloudfront.net\/images\/tarsier.png\" alt=\"\" \/><\/p>\n\n<p>Later on, a very similar architecture was added as an official example <a href=\"https:\/\/github.com\/keras-team\/keras\/blob\/master\/examples\/lstm_text_generation.py\">in Keras repository<\/a>.<\/p>\n\n<p>There are a lot of errors opened, discussing the model seems \u201cuntrained\u201d:<\/p>\n\n<p><a href=\"https:\/\/stackoverflow.com\/questions\/46119435\/keras-lstm-why-different-results-with-same-model-same-weights\">one<\/a> <a href=\"https:\/\/stackoverflow.com\/questions\/48562099\/keras-why-does-sequential-and-model-give-different-outputs\">two<\/a> <a href=\"https:\/\/github.com\/keras-team\/keras\/issues\/4875\">three<\/a> <a href=\"https:\/\/stackoverflow.com\/questions\/44509069\/lstm-model-prints-trailing-garbage-characters\">and<\/a> <a href=\"https:\/\/stackoverflow.com\/questions\/51809132\/how-to-restore-a-saved-model-with-lstm-layers-in-keras\">so<\/a> <a href=\"https:\/\/github.com\/ChunML\/text-generator\/issues\/4\">on<\/a>\u2026<\/p>\n\n<p>The original code can be found <a href=\"https:\/\/github.com\/ChunML\/text-generator\/\">on github<\/a>, but here I will provide both the code and my own vision on how to fix the errors.<\/p>\n\n<h2 id=\"fixing-reproducibility\">Fixing reproducibility<\/h2>\n\n<p>Fisrt of all, <strong>spoiler<\/strong> the error is not in the main code, but in load_data function, that makes a set of chars of your data:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># method for preparing the training data\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">load_data<\/span><span class=\"p\">(<\/span><span class=\"n\">data_dir<\/span><span class=\"p\">,<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">data_dir<\/span><span class=\"p\">,<\/span> <span class=\"s\">'r'<\/span><span class=\"p\">).<\/span><span class=\"n\">read<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">chars<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">))<\/span> <span class=\"c1\"># &lt;---- HERE IT IS!\n<\/span>    <span class=\"n\">VOCAB_SIZE<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">ix_to_char<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"n\">ix<\/span><span class=\"p\">:<\/span><span class=\"n\">char<\/span> <span class=\"k\">for<\/span> <span class=\"n\">ix<\/span><span class=\"p\">,<\/span> <span class=\"n\">char<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">enumerate<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)}<\/span>\n    <span class=\"n\">char_to_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"n\">char<\/span><span class=\"p\">:<\/span><span class=\"n\">ix<\/span> <span class=\"k\">for<\/span> <span class=\"n\">ix<\/span><span class=\"p\">,<\/span> <span class=\"n\">char<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">enumerate<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>The chars list is different all the time you pass a new text file, and the dictionary of symbol indexes ({0:\u2019a\u2019, 1:\u2019b\u2019,2:\u2019c\u2019\u2026}) that you pass yet your model does not match with the one it was trained on. To escape that error, just save the ix_to_char explicitly as pickle, json or text and pass it to generate_text function when loading your model.<\/p>\n\n<h2 id=\"adding-more-info\">Adding more info<\/h2>\n\n<p>When providing little  data to a generator like this one (and by little data I mean you are providing a few books, not a big webcorpus), in fact, you are waiting for your model to overfit. This way it can learn to reproduce words from the texts, but not to produce new ones, unless you are working with an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Agglutinative_language\">agglutinative language<\/a> (English is not one of those!).<\/p>\n\n<p><img src=\"https:\/\/github.com\/TatianaShavrina\/blog\/blob\/master\/assets\/img\/generate-3_photo-resizer.ru.png\" alt=\"\" \/><\/p>\n\n<p>So, if you are not limited to the speed of learning, you better provide more complete information about your texts.\nAs you could see in the original post, the model gets each n-gram of symbols with some step:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span><span class=\"o\">\/<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span> \n\t<span class=\"n\">X_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">:(<\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">]<\/span> \n\t<span class=\"n\">X_sequence_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">char_to_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">value<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">value<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">X_sequence<\/span><span class=\"p\">]<\/span>\n\t<span class=\"n\">input_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n\t<span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n\t\t <span class=\"n\">input_sequence<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">][<\/span><span class=\"n\">X_sequence_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.<\/span>\n\t\t<span class=\"n\">X<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">input_sequence<\/span>\n\n\t<span class=\"n\">y_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">:(<\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span>\n<span class=\"n\">y_sequence_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">char_to_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">value<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">value<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">y_sequence<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div><\/div>\n<p>Let\u2019s see how the sequences are formed: \nfor each step i, which can get value from 0 to (symbol length of the data)\/(sequence length), we get sequences  with no character overlap: 0-80, 80-160, 160-240 and so on.<\/p>\n\n<p><strong>This is a complete barbarism using language data!<\/strong><\/p>\n\n<p><img src=\"https:\/\/us.123rf.com\/450wm\/typau\/typau1712\/typau171200007\/91174110-stock-illustration-black-and-white-engrave-isolated-tarsier-illustration.jpg?ver=6\" alt=\"\" title=\"Omg I'm so shook\" \/><\/p>\n\n<p>With this kind of overlap you are using only 1\/(sequence length) - 1\/80 in our case - of the actual data. Anyone who has ever used n-grams knows that the language model performs better if you provide it with full sequential information: 0-80, 1-81, 2-82, etc.<\/p>\n\n<p>To prevent excessive learning slowdown, which you will inevitably face when using an overlap of 1 symbol,  you can declare a \u2018step\u2019 variable which would stand for an overlap length you want:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">step<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">3<\/span>\n<span class=\"n\">l<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">i<\/span> <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">-<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">step<\/span><span class=\"p\">)]<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">)):<\/span> \n    <span class=\"n\">X_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]:<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">]<\/span> \n    <span class=\"n\">X_sequence_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">char_to_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">value<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">value<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">X_sequence<\/span><span class=\"p\">]<\/span>\n    <span class=\"n\">input_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">input_sequence<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">][<\/span><span class=\"n\">X_sequence_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.<\/span>\n        <span class=\"n\">X<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">input_sequence<\/span>\n\n    <span class=\"n\">y_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"n\">seq_length<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div><\/div>\n<p>With this example, you are getting an overlap of 0-80, 3-83, 6-86, etc.<\/p>\n\n<p>As this infers lso the length of the vector you are getting, don\u2019t forget to change the length of X and Y:<\/p>\n\n<p>from:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">X<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span><span class=\"o\">\/<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n<span class=\"n\">y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span><span class=\"o\">\/<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span><span class=\"o\">\/<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n\t<span class=\"n\">X_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">:(<\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"o\">*<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>to:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">X<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">),<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span> <span class=\"c1\">#(len(data) - seq_length)\/\/step\n<\/span><span class=\"n\">y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">),<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">)):<\/span> \n    <span class=\"n\">X_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]:<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">]<\/span> \n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"further-tuning\">Further tuning<\/h2>\n\n<p>The better you know your data the better is the model. As usual in deep learning, you should check the quality of the model after every N iterations - for example, check the generated output after every 100 epochs:<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">if<\/span> <span class=\"n\">nb_epoch<\/span> <span class=\"o\">%<\/span> <span class=\"mi\">10<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"mi\">20<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>You can adjust the length of the context your model is looking at - in English, the basic parameter value is from 40 to 80 symbols.<\/p>\n\n<p><img src=\"https:\/\/github.com\/TatianaShavrina\/blog\/blob\/master\/assets\/img\/1_gCWUibmQ8rszKxI3G19KmA._photo-resizer.ru.jpeg\" alt=\"\" \/><\/p>\n\n<h2 id=\"primary-results\">Primary results<\/h2>\n\n<p>With this architecture, a really human-like result can be achieved on a small data. As we have fixed main issues, you can now save it and use in production.<\/p>\n\n<p>I tried to make a simple Telegram-bot, which generates proverbs \u201cof different cultures\u201d - Armenian, Indian, Sufi, Hasidic and Jewish (all in Russian) - you can find all the source code <a href=\"https:\/\/github.com\/TatianaShavrina\/NeuroBasnya\/\">in my repository<\/a>.<\/p>\n\n<p>Have fun!<\/p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># coding: utf-8\n<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">__future__<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">print_function<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">matplotlib.pyplot<\/span> <span class=\"k\">as<\/span> <span class=\"n\">plt<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">numpy<\/span> <span class=\"k\">as<\/span> <span class=\"n\">np<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">time<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">csv<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">keras.models<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Sequential<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">keras.layers.core<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Dense<\/span><span class=\"p\">,<\/span> <span class=\"n\">Activation<\/span><span class=\"p\">,<\/span> <span class=\"n\">Dropout<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">keras.layers.recurrent<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">LSTM<\/span><span class=\"p\">,<\/span> <span class=\"n\">SimpleRNN<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">keras.layers.wrappers<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">TimeDistributed<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">__future__<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">print_function<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">numpy<\/span> <span class=\"k\">as<\/span> <span class=\"n\">np<\/span>\n\n<span class=\"c1\"># method for generating text\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"n\">length<\/span><span class=\"p\">,<\/span> <span class=\"n\">vocab_size<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">):<\/span>\n    <span class=\"c1\"># starting with random character\n<\/span>    <span class=\"n\">ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">random<\/span><span class=\"p\">.<\/span><span class=\"n\">randint<\/span><span class=\"p\">(<\/span><span class=\"n\">vocab_size<\/span><span class=\"p\">)]<\/span>\n    <span class=\"n\">y_char<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">ix_to_char<\/span><span class=\"p\">[<\/span><span class=\"n\">ix<\/span><span class=\"p\">[<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">]]]<\/span>\n    <span class=\"n\">X<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">length<\/span><span class=\"p\">,<\/span> <span class=\"n\">vocab_size<\/span><span class=\"p\">))<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">length<\/span><span class=\"p\">):<\/span>\n        <span class=\"c1\"># appending the last predicted character to sequence\n<\/span>        <span class=\"n\">X<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">i<\/span><span class=\"p\">,<\/span> <span class=\"p\">:][<\/span><span class=\"n\">ix<\/span><span class=\"p\">[<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">1<\/span>\n        <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">ix_to_char<\/span><span class=\"p\">[<\/span><span class=\"n\">ix<\/span><span class=\"p\">[<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">]],<\/span> <span class=\"n\">end<\/span><span class=\"o\">=<\/span><span class=\"s\">\"\"<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">ix<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">argmax<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">predict<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">[:,<\/span> <span class=\"p\">:<\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"p\">:])[<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">y_char<\/span><span class=\"p\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">ix_to_char<\/span><span class=\"p\">[<\/span><span class=\"n\">ix<\/span><span class=\"p\">[<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">]])<\/span>\n    \n    \n    <span class=\"k\">return<\/span> <span class=\"p\">(<\/span><span class=\"s\">''<\/span><span class=\"p\">).<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">y_char<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\"># method for preparing the training data\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">load_data<\/span><span class=\"p\">(<\/span><span class=\"n\">data_dir<\/span><span class=\"p\">,<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">data_dir<\/span><span class=\"p\">,<\/span> <span class=\"s\">'r'<\/span><span class=\"p\">).<\/span><span class=\"n\">read<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">chars<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">VOCAB_SIZE<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'Data length: {} characters'<\/span><span class=\"p\">.<\/span><span class=\"nb\">format<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)))<\/span>\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'Vocabulary size: {} characters'<\/span><span class=\"p\">.<\/span><span class=\"nb\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">ix_to_char<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"n\">ix<\/span><span class=\"p\">:<\/span><span class=\"n\">char<\/span> <span class=\"k\">for<\/span> <span class=\"n\">ix<\/span><span class=\"p\">,<\/span> <span class=\"n\">char<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">enumerate<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)}<\/span>\n    <span class=\"n\">char_to_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"n\">char<\/span><span class=\"p\">:<\/span><span class=\"n\">ix<\/span> <span class=\"k\">for<\/span> <span class=\"n\">ix<\/span><span class=\"p\">,<\/span> <span class=\"n\">char<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">enumerate<\/span><span class=\"p\">(<\/span><span class=\"n\">chars<\/span><span class=\"p\">)}<\/span>\n    <span class=\"n\">step<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">3<\/span>\n    <span class=\"n\">l<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">i<\/span> <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">-<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">step<\/span><span class=\"p\">)]<\/span>\n    <span class=\"n\">X<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">),<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span> <span class=\"c1\">#(len(data) - seq_length)\/\/step\n<\/span>    <span class=\"n\">y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">),<\/span> <span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n\n    <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">)):<\/span> \n        <span class=\"n\">X_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]:<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">]<\/span> \n        <span class=\"n\">X_sequence_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">char_to_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">value<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">value<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">X_sequence<\/span><span class=\"p\">]<\/span>\n        <span class=\"n\">input_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n            <span class=\"c1\">#print(i)\n<\/span>            <span class=\"n\">input_sequence<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">][<\/span><span class=\"n\">X_sequence_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.<\/span>\n            <span class=\"n\">X<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">input_sequence<\/span>\n\n        <span class=\"n\">y_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"n\">l<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">+<\/span><span class=\"n\">seq_length<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span>\n        <span class=\"n\">y_sequence_ix<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">char_to_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">value<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">value<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">y_sequence<\/span><span class=\"p\">]<\/span>\n        <span class=\"n\">target_sequence<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"p\">.<\/span><span class=\"n\">zeros<\/span><span class=\"p\">((<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">))<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">seq_length<\/span><span class=\"p\">):<\/span>\n            <span class=\"n\">target_sequence<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">][<\/span><span class=\"n\">y_sequence_ix<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.<\/span>\n            <span class=\"n\">y<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">target_sequence<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">X<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span>\n\n<span class=\"n\">DATA_DIR<\/span> <span class=\"o\">=<\/span><span class=\"s\">\"\/home\/yourdir\/text.txt\"<\/span>\n<span class=\"n\">BATCH_SIZE<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">128<\/span>\n<span class=\"n\">HIDDEN_DIM<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">500<\/span>\n<span class=\"n\">SEQ_LENGTH<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">80<\/span>\n<span class=\"n\">WEIGHTS<\/span> <span class=\"o\">=<\/span> <span class=\"s\">\"\"<\/span>\n<span class=\"n\">MODE<\/span> <span class=\"o\">=<\/span> <span class=\"s\">'train'<\/span>\n\n<span class=\"n\">GENERATE_LENGTH<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">500<\/span>\n<span class=\"n\">LAYER_NUM<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">2<\/span>\n\n<span class=\"c1\"># Creating training data\n<\/span><span class=\"n\">X<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span> <span class=\"o\">=<\/span> <span class=\"n\">load_data<\/span><span class=\"p\">(<\/span><span class=\"n\">DATA_DIR<\/span><span class=\"p\">,<\/span> <span class=\"n\">SEQ_LENGTH<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\"># Creating and compiling the Network\n<\/span><span class=\"n\">model<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Sequential<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">LSTM<\/span><span class=\"p\">(<\/span><span class=\"n\">HIDDEN_DIM<\/span><span class=\"p\">,<\/span> <span class=\"n\">input_shape<\/span><span class=\"o\">=<\/span><span class=\"p\">(<\/span><span class=\"bp\">None<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">),<\/span> <span class=\"n\">return_sequences<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">))<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">LAYER_NUM<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">LSTM<\/span><span class=\"p\">(<\/span><span class=\"n\">HIDDEN_DIM<\/span><span class=\"p\">,<\/span> <span class=\"n\">return_sequences<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">TimeDistributed<\/span><span class=\"p\">(<\/span><span class=\"n\">Dense<\/span><span class=\"p\">(<\/span><span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">)))<\/span>\n    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">Activation<\/span><span class=\"p\">(<\/span><span class=\"s\">'softmax'<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"nb\">compile<\/span><span class=\"p\">(<\/span><span class=\"n\">loss<\/span><span class=\"o\">=<\/span><span class=\"s\">\"categorical_crossentropy\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">optimizer<\/span><span class=\"o\">=<\/span><span class=\"s\">\"rmsprop\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\"># Generate some sample before training to know how bad it is!\n<\/span><span class=\"n\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"mi\">150<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">WEIGHTS<\/span> <span class=\"o\">==<\/span> <span class=\"s\">''<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">load_weights<\/span><span class=\"p\">(<\/span><span class=\"n\">WEIGHTS<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">nb_epoch<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">int<\/span><span class=\"p\">(<\/span><span class=\"n\">WEIGHTS<\/span><span class=\"p\">[<\/span><span class=\"n\">WEIGHTS<\/span><span class=\"p\">.<\/span><span class=\"n\">rfind<\/span><span class=\"p\">(<\/span><span class=\"s\">'_'<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"n\">WEIGHTS<\/span><span class=\"p\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"s\">'.'<\/span><span class=\"p\">)])<\/span>\n<span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">nb_epoch<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n\n<span class=\"c1\"># Training if there is no trained weights specified\n<\/span><span class=\"k\">if<\/span> <span class=\"n\">MODE<\/span> <span class=\"o\">==<\/span> <span class=\"s\">'train'<\/span> <span class=\"ow\">or<\/span> <span class=\"n\">WEIGHTS<\/span> <span class=\"o\">==<\/span> <span class=\"s\">''<\/span><span class=\"p\">:<\/span>\n    <span class=\"k\">while<\/span> <span class=\"bp\">True<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s\">Epoch: {}<\/span><span class=\"se\">\\n<\/span><span class=\"s\">'<\/span><span class=\"p\">.<\/span><span class=\"nb\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">nb_epoch<\/span><span class=\"p\">))<\/span>\n        <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">fit<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"n\">batch_size<\/span><span class=\"o\">=<\/span><span class=\"n\">BATCH_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">verbose<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">nb_epoch<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">nb_epoch<\/span> <span class=\"o\">+=<\/span> <span class=\"mi\">1<\/span>\n        <span class=\"n\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"mi\">100<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">nb_epoch<\/span> <span class=\"o\">%<\/span> <span class=\"mi\">10<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">save_weights<\/span><span class=\"p\">(<\/span><span class=\"s\">'\/home\/workdir\/models\/model_checkpoint_layer_{}_hidden_{}_epoch_{}.hdf5'<\/span><span class=\"p\">.<\/span><span class=\"nb\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">LAYER_NUM<\/span><span class=\"p\">,<\/span> <span class=\"n\">HIDDEN_DIM<\/span><span class=\"p\">,<\/span> <span class=\"n\">nb_epoch<\/span><span class=\"p\">))<\/span>\n            <span class=\"n\">json_string<\/span> <span class=\"o\">=<\/span> <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">to_json<\/span><span class=\"p\">()<\/span>\n            <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"sa\">r<\/span><span class=\"s\">\"\/home\/workdir\/models\/model.json\"<\/span><span class=\"p\">,<\/span> <span class=\"s\">\"w\"<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">text_file<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">text_file<\/span><span class=\"p\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">json_string<\/span><span class=\"p\">)<\/span>\n\t    <span class=\"n\">charset<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s\">''<\/span><span class=\"p\">,<\/span> <span class=\"s\">'w'<\/span><span class=\"p\">,<\/span> <span class=\"n\">encoding<\/span><span class=\"o\">=<\/span><span class=\"s\">'utf-8'<\/span><span class=\"p\">).<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s\">''<\/span><span class=\"p\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">([<\/span><span class=\"n\">ix_to_char<\/span><span class=\"p\">[<\/span><span class=\"n\">c<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">c<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">]))<\/span>\n\t    <span class=\"n\">charset<\/span><span class=\"p\">.<\/span><span class=\"n\">close<\/span><span class=\"p\">()<\/span>\n\t\t\n\n<span class=\"c1\"># Else, loading the trained weights and performing generation only\n<\/span><span class=\"k\">elif<\/span> <span class=\"n\">WEIGHTS<\/span> <span class=\"o\">==<\/span> <span class=\"s\">''<\/span><span class=\"p\">:<\/span>\n  <span class=\"c1\"># Loading the trained weights\n<\/span>    <span class=\"n\">model<\/span><span class=\"p\">.<\/span><span class=\"n\">load_weights<\/span><span class=\"p\">(<\/span><span class=\"n\">WEIGHTS<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"n\">GENERATE_LENGTH<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s\">'<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">'<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s\">Nothing to do!'<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\"># Generate some sample  to know how bad it is!\n<\/span><span class=\"n\">generate_text<\/span><span class=\"p\">(<\/span><span class=\"n\">model<\/span><span class=\"p\">,<\/span> <span class=\"mi\">15<\/span><span class=\"p\">,<\/span> <span class=\"n\">VOCAB_SIZE<\/span><span class=\"p\">,<\/span> <span class=\"n\">ix_to_char<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p><img src=\"https:\/\/ih0.redbubble.net\/image.362717865.4299\/flat,550x550,075,f.u2.jpg\" alt=\"\" \/><\/p>","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"category":[{"@attributes":{"term":"NLP"}},{"@attributes":{"term":"keras"}},{"@attributes":{"term":"deep learning"}},{"@attributes":{"term":"text generation"}},{"@attributes":{"term":"python"}}],"summary":"Building a simple and nice text generator in Keras is not a difficult task, yet there are a few mistakes in the framework, that prevent you from succeeding."},{"title":"5 weird tricks for a good spell-checker","link":{"@attributes":{"href":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/spellcheck\/","rel":"alternate","type":"text\/html","title":"5 weird tricks for a good spell-checker"}},"published":"2018-08-30T00:00:00+00:00","updated":"2018-08-30T00:00:00+00:00","id":"https:\/\/tatianashavrina.github.io\/\/2018\/08\/30\/spellcheck","content":"<p><strong>A quick intro<\/strong><\/p>\n\n<p>It is more than 10 years already Peter Norvig has put his sterlingly simple spell-checker into 21 line of <a href=\"https:\/\/norvig.com\/spell-correct.html\">python code<\/a>. A pure probability model selects the most probable replacement for an unknown word from the list of words collected in a book \u2013 this minimalistic approach is still quite relevant and can be considered as a baseline and a prototype for further development, however, it has a number of imperfections:<\/p>\n\n<ol>\n  <li>if your dictionary is big enough, (like millions of words), the candidate list will be too big to rank it properly with this simple technique<\/li>\n  <li>no restrictions on the speed and memory are provided for this solution<\/li>\n  <li>it cannot find real-word errors<\/li>\n<\/ol>\n\n<p>We could name, of course, other problems of the original code, but let us consider these two the main ones, as this is just a prototype \u2013 and we should now clarify, how to adapt the algorithm so it can become a production solution.<\/p>\n\n<p>So, if we just want our spell-checker to work fast despite having a big dictionary (what is a usual situation, when you collect a dictionary from a big web-corpus or your language has rich morphology \u2013 like Russian, Hungarian, etc), then you have to search for a more courteous manner to refine the dictionary storage and time a new word is compared to dictionary words you have.<\/p>\n\n<p>I will now try to introduce 5 new algorithms and data structures that I use to optimize spell-checking.<\/p>\n\n<p>So, getting started: there are two steps in spelling correction:<\/p>\n<ul>\n  <li>firstly, you get a dictionary and find errors in the text,<\/li>\n  <li>secondly, you choose the best correction, ranking dictionary candidates<\/li>\n<\/ul>\n\n<h2 id=\"storing-a-dictionary\">Storing a dictionary<\/h2>\n\n<p>Searching if a word is in a plain dictionary or list structure can take a while, and it can take even more if you should measure a distance between an out-of-vocabulary word and every word in a dictionary to find corrections.<\/p>\n\n<h3 id=\"1-trie\">1. Trie<\/h3>\n<p>Trie, aka radix tree or prefix tree, is a kind of search tree \u2014 an ordered tree data structure used to store a dynamic set or associative array where the keys are usually strings. It is kind of similar to a binary search tree, but for language data - you have no &gt;&lt; conditions, but variants - what substring would be the next:\n<img src=\"https:\/\/i.stack.imgur.com\/f9Q3u.jpg\" alt=\"\" \/><\/p>\n\n<p>So, all the descendants of a node have a common prefix of the string associated with that node, and the root is associated with the empty string. In Python kind of logic, that corresponds to a recursive dictionary in a dictionary, where keys are letters. This kind of structure is extremely useful when you have to store a dictionary in memory to check if a word is in dictionary - you do not only the amount of memory, but also the search time decreases tens of times, as the search for a key in a dictionary of millions of words is orders of magnitude slower than the sequential search in that descending embedded dictionaries.<\/p>\n\n<p>You can build an independent trie-vocabulary for every list of words beginning with the same letter (A-, B-, etc), or having this letter last, etc - and thus limit the search for candidates only by these trie-structures.<\/p>\n\n<p>Realizations:<\/p>\n\n<p><a href=\"https:\/\/github.com\/pytries\/datrie\">Python<\/a><\/p>\n\n<p><a href=\"https:\/\/linux.thai.net\/~thep\/datrie\/datrie.html\">C<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/digitalstain\/DoubleArrayTrie\">Java<\/a><\/p>\n\n<h2 id=\"approximate-string-matching\">Approximate string matching<\/h2>\n\n<p>Now let\u2019s move to tricks with word distance and optimal candidate ranging.<\/p>\n\n<h3 id=\"2-bk-trees\">2. Bk-trees<\/h3>\n\n<p>A BK-tree is a metric tree suggested by Walter Austin Burkhard and Robert M. Keller[1] specifically adapted to discrete metric spaces. For simplicity, let us consider integer discrete metric d (x, y). Then, BK-tree is defined in the following way: an arbitrary element a is selected as a root node. The root node may have zero or more subtrees. The k-th subtree is recursively built of all elements b such that d(a,b)=k. BK-trees can be used for approximate string matching in a dictionary:<\/p>\n\n<p><img src=\"https:\/\/nullwords.files.wordpress.com\/2013\/03\/bk31-e1363207034407.png\" alt=\"\" \/><\/p>\n\n<p>The main profit of using bk-trees instead of plain dictionaries is that measuring the distance between an out-of-vocabulary word and every word in the dictionary is much faster in this kind of structure - it\u2019s now O(log n) instead of O(n).<\/p>\n\n<p>Implementations:<\/p>\n\n<p><a href=\"https:\/\/github.com\/Jetsetter\/pybktree\">Python<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/tyler\/BkTree\">C<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/threedaymonk\/bktree\">Ruby<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/gtri\/bk-tree\">Java<\/a><\/p>\n\n<h3 id=\"3-phonetic-algorithms\">3. Phonetic Algorithms<\/h3>\n\n<p>Phonetic algorithms are widely used in spellchecking, as they can make the search of a close vocabulary word much more precise:<\/p>\n<ul>\n  <li>if you use some standard distance measure, which is based on letter alignment, the search is  usually limited to candidates standing 1-2 letters from a word with an error. If you increase this distance, you probably get too many of irrelevant candidates.<\/li>\n  <li>a lot of typical errors, caused by intentional distortion or slangy language gamification, outstand from a relevant candidate  more than 2 letters away: <em>riiiiigtht<\/em> \u2013&gt; <em>right<\/em>, <em>donut<\/em> \u2013&gt; <em>doughnut<\/em>, <em>ave<\/em>\u2013&gt; <em>avenue<\/em><\/li>\n  <li>\n    <p>these far-from-a-right-candidate examples seem to be somehow systematically located: actually, these errors we can call \u201cphonetic\u201d or \u201cabbreviative\u201d, and instead of using ordinary distance measures we can make a new, phonetic distance. Phonetic algorithms solve this problem quite well.<\/p>\n  <\/li>\n  <li><strong>Metaphone<\/strong><\/li>\n<\/ul>\n\n<p>Metaphone algorithm [2]-[3] is one of the most popular phonetic algorithms for spelling correction. \nIt makes a phonetic hash out of each word in the vocabulary and getting this kind of a hash for a word with error, you can search for a better candidate through your hashed dictionary using standard distance measures.<\/p>\n\n<p>Original Metaphone codes use the 16 consonant symbols 0BFHJKLMNPRSTWXY. The \u20180\u2019 represents \u201cth\u201d (as an ASCII approximation of \u0398), \u2018X\u2019 represents \u201csh\u201d or \u201cch\u201d, and the others represent their usual English pronunciations. The vowels AEIOU are also used, but only at the beginning of the code. Text below summarizes most of the rules in the original implementation:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Drop duplicate adjacent letters, except for C.\nIf the word begins with 'KN', 'GN', 'PN', 'AE', 'WR', drop the first letter.\nDrop 'B' if after 'M' at the end of the word.\n'C' transforms to 'X' if followed by 'IA' or 'H' (unless in latter case, it is part of '-SCH-', in which case it transforms to 'K'). 'C' transforms to 'S' if followed by 'I', 'E', or 'Y'. Otherwise, 'C' transforms to 'K'.\n'D' transforms to 'J' if followed by 'GE', 'GY', or 'GI'. Otherwise, 'D' transforms to 'T'.\nDrop 'G' if followed by 'H' and 'H' is not at the end or before a vowel. Drop 'G' if followed by 'N' or 'NED' and is at the end.\n'G' transforms to 'J' if before 'I', 'E', or 'Y', and it is not in 'GG'. Otherwise, 'G' transforms to 'K'.\nDrop 'H' if after vowel and not before a vowel.\n'CK' transforms to 'K'.\n'PH' transforms to 'F'.\n'Q' transforms to 'K'.\n'S' transforms to 'X' if followed by 'H', 'IO', or 'IA'.\n'T' transforms to 'X' if followed by 'IA' or 'IO'. 'TH' transforms to '0'. Drop 'T' if followed by 'CH'.\n'V' transforms to 'F'.\n'WH' transforms to 'W' if at the beginning. Drop 'W' if not followed by a vowel.\n'X' transforms to 'S' if at the beginning. Otherwise, 'X' transforms to 'KS'.\nDrop 'Y' if not followed by a vowel.\n'Z' transforms to 'S'.\nDrop all vowels unless it is the beginning.\n<\/code><\/pre><\/div><\/div>\n\n<p>The Double Metaphone phonetic encoding algorithm is the second generation of this algorithm. It makes a number of fundamental design improvements over the original Metaphone algorithm -  it uses a much more complex ruleset for coding than its predecessor; for example, it tests for approximately 100 different contexts of the use of the letter C alone.<\/p>\n\n<p>Implementations: (you can write rules for your own language with implementations below)<\/p>\n\n<p><a href=\"https:\/\/sourceforge.net\/projects\/metaphoneptbr\/\">Brazilian Portuguese in C<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/ruliana\/MTFN\">Brazilian Portuguese in Java<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/amsqr\/Spanish-Metaphone\">Spanish in Python<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/pavlo\/russian_metaphone\">Russian Metaphone in Ruby<\/a><\/p>\n\n<ul>\n  <li><strong>Match rating approach<\/strong><\/li>\n<\/ul>\n\n<p>Match rating approach (MRA) is a phonetic algorithm developed in 1977 and firstly used for the indexation and comparison of homophonous names. Unlike Metaphone, MRA includes both hashing rules and their distance measure. \nIt is suitable for small vocabularies and searching for abbreviations and acronyms.<\/p>\n\n<p>Encoding rules<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Delete all vowels unless the vowel begins the word\nRemove the second consonant of any double consonants present\nReduce codex to 6 letters by joining the first 3 and last 3 letters only\n<\/code><\/pre><\/div><\/div>\n\n<p>Comparison rules<\/p>\n\n<p>In this section, the words \u201cstring(s)\u201d and \u201cname(s)\u201d mean \u201cencoded string(s)\u201d and \u201cencoded name(s)\u201d.<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>If the length difference between the encoded strings is 3 or greater, then no similarity comparison is done.\nObtain the minimum rating value by calculating the length sum of the encoded strings and using table A\nProcess the encoded strings from left to right and remove any identical characters found from both strings respectively.\nProcess the unmatched characters from right to left and remove any identical characters found from both names respectively.\nSubtract the number of unmatched characters from 6 in the longer string. This is the similarity rating.\nIf the similarity rating equal to or greater than the minimum rating then the match is considered good.\n<\/code><\/pre><\/div><\/div>\n\n<p>Implementations:<\/p>\n\n<p><a href=\"https:\/\/archive.codeplex.com\/?p=sounditout\">C#<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/nodef\/english-mraphonetic\">Javascript<\/a><\/p>\n\n<h3 id=\"4-distance-measures\">4. Distance measures<\/h3>\n\n<p>There are some non-standard measures of string distance besides Levenshtein distance, which are calculated using a different set of allowable edit operations. Depending on which mistakes you want to correct, while ranking the candidates using the distance function, you can choose more sophisticated distance metric.  For instance, there are<\/p>\n\n<ul>\n  <li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Jaro%E2%80%93Winkler_distance\">Jaro\u2013Winkler Similarity<\/a> measures the minimum number of single-character transpositions required to change one word into the other, giving more favorable ratings to strings that match from the beginning.<\/li>\n  <li>the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Levenshtein_distance\">Levenshtein distance<\/a> allows deletion, insertion and substitution;<\/li>\n  <li>the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Damerau%E2%80%93Levenshtein_distance\">Damerau\u2013Levenshtein distance<\/a> allows insertion, deletion, substitution, and the transposition of two adjacent characters;<\/li>\n  <li>the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Longest_common_subsequence_problem\">longest common subsequence (LCS)<\/a> distance allows only insertion and deletion, not substitution;<\/li>\n  <li>the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Hamming_distance\">Hamming distance<\/a> allows only substitution, hence, it only applies to strings of the same length.<\/li>\n<\/ul>\n\n<p>You can, of course, implement your own function - for example, take Damerau\u2013Levenshtein distance as a baseline and add some special operations to it, like rearranging the candidates according to keyboard distances.<\/p>\n\n<h2 id=\"finding-real-word-errors\">Finding real-word errors<\/h2>\n\n<h3 id=\"5-machine-learning-for-spelling\">5. Machine Learning for spelling<\/h3>\n\n<p>\u201cSee you in five minuets\u201d - this type of errors, when a spelling error leads to an appearance of another normal word, is called Real-Word Error.\nReal-word errors (RWE) are harder to find, as you cannot spot them with a dictionary lookup, but the most common approach to finding them is context-based.<\/p>\n\n<p>I will not recommend this type of algorithm for spellcheckers with real-time speed limits, as rechecking every word in a sentence and deciding whether the context is typical for it or not, is not a fast way to check the spelling.<\/p>\n\n<p>Yet sometimes RWE are about 15% of all the errors, and in this case, you  should deal with them some way. Most successful model for dealing with RWEs is described in [4] - there you can find pseudocode based on word ngrams as a context. Another way to find RWEs is the  noisy channel model, which is not considered a classic approach anymore, yet can be effective in this kind of error detection.<\/p>\n\n<p>Implementations:<\/p>\n\n<p><a href=\"http:\/\/old.atala.org\/IMG\/pdf\/Flor-TAL53-3.pdf\">Pseudo-Java<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/ofrik\/NLP-HW1\/blob\/master\/spell_checker.py\">Noisy-channel Python<\/a><\/p>\n\n<p><a href=\"https:\/\/github.com\/UKPLab\/spelling-experiments\">Another solution for Java<\/a><\/p>\n\n<h2 id=\"references\">References<\/h2>\n\n<p>[1] <a href=\"https:\/\/dl.acm.org\/citation.cfm?doid=362003.362025\">W. Burkhard and R. Keller. Some approaches to best-match file searching, CACM, 1973<\/a><\/p>\n\n<p>[2] <a href=\"https:\/\/scinapse.io\/papers\/152126889\">Hanging on the Metaphone, Lawrence Philips. Computer Language, Vol. 7, No. 12 (December), 1990.<\/a><\/p>\n\n<p>[3] <a href=\"http:\/\/www.drdobbs.com\/the-double-metaphone-search-algorithm\/184401251?pgno=2\">The Double Metaphone Search Algorithm, By Lawrence Phillips, June 1, 2000, Dr Dobb\u2019s<\/a><\/p>\n\n<p>[4] <a href=\"http:\/\/old.atala.org\/IMG\/pdf\/Flor-TAL53-3.pdf\">Flor M.(2012) Four types of context for automatic spelling correction \/\/TAL.\u2014Vol. 53.\u2014Vol. 3.\u2014pp. 61\u201399.<\/a><\/p>","author":{"name":"Tatiana Shavrina","email":"<rybolos@gmail.com>"},"category":[{"@attributes":{"term":"NLP"}},{"@attributes":{"term":"spellchecking"}},{"@attributes":{"term":"spelling correction"}}],"summary":"A quick intro"}]}