{"id":2672,"date":"2018-05-02T16:00:10","date_gmt":"2018-05-02T16:00:10","guid":{"rendered":"https:\/\/rocketloop.de\/?p=2672"},"modified":"2021-08-02T08:38:24","modified_gmt":"2021-08-02T08:38:24","slug":"preprocessing-of-text-for-machine-learning-in-node-js","status":"publish","type":"post","link":"https:\/\/rocketloop.de\/de\/blog\/vorverarbeitung-von-text-fuer-machine-learning-in-node-js\/","title":{"rendered":"Vorverarbeitung von Text f\u00fcr Machine Learning in node.js"},"content":{"rendered":"<p>F\u00fcr die Verwendung gro\u00dfer Daten f\u00fcr Machine Learning Verfahren bedarf es erst einer sorgf\u00e4ltigen Vorverarbeitung, die grundlegend ist f\u00fcr die Genauigkeit der Ausgabe. Betrachten wir die Klassifizierung von Texten auf Wortebene, so ist es wichtig, dass jegliche Texte vorverarbeitet werden, sprich jeder Text wird in S\u00e4tze unterteilt und dieser danach in einzelne W\u00f6rter. Der OpenNLP Wrapper f\u00fcr Node.js ist optimal daf\u00fcr geeignet diese Vorverarbeitung zu leisten, denn dieser ist nicht nur detailliert konfigurierbar, sondern unterliegt auch der MIT License, sodass der private und kommerzielle Gebrauch kostenlos ist.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"installation\">Installation<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\">npm install opennlp<\/pre>\n\n\n\n<p>OpenNLP bietet folgende Verfahren einen Text zu verarbeiten\/analysieren:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Chunker<\/li><li>Name Finder<\/li><li>Part of Speach Tagging<\/li><li>Sentence Detector<\/li><li>Tokenizer<\/li><\/ul>\n\n\n\n<p class=\" translation-block\">Daf\u00fcr stellt Apache unter \u00a0<a href=\"http:\/\/opennlp.sourceforge.net\/models-1.5\/\" target=\"_self\">http:\/\/opennlp.sourceforge.net\/models-1.5\/<\/a> eine Vielzahl trainierter Modelle zur Verf\u00fcgung, die in verschiedenen Sprachen (zum Beispiel Englisch und Deutsch) heruntergeladen und eingebunden werden k\u00f6nnen. Zus\u00e4tzlich ben\u00f6tigt man die opennlp-tools-1.6.0.jar, die <a href=\"https:\/\/opennlp.apache.org\/announcement\/release-160.html\" data-type=\"URL\" data-id=\"https:\/\/opennlp.apache.org\/announcement\/release-160.html\" target=\"_self\">hier<\/a> heruntergeladen werden kann.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"konfiguration\">Konfiguration<\/h2>\n\n\n\n<p>In der folgenden Konfiguration wird der OpenNLP Wrapper mit den deutschen Modellen f\u00fcr den Sentence Detector und den Tokenizer initialisiert.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> openNLPOptions = {\n    <span class=\"hljs-string\">\"models\"<\/span> : {\n        <span class=\"hljs-string\">\"tokenizer\"<\/span>: nlpPath + <span class=\"hljs-string\">'\/de-token.bin'<\/span>,\n        <span class=\"hljs-string\">\"sentenceDetector\"<\/span>: nlpPath + <span class=\"hljs-string\">'\/de-sent.bin'<\/span>,\n    },\n    <span class=\"hljs-string\">\"jarPath\"<\/span>: nlpPath + <span class=\"hljs-string\">\"\/opennlp-tools-1.6.0.jar\"<\/span>\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Der gro\u00dfe Vorteil hierbei ist, dass f\u00fcr die Verarbeitung einer anderen Sprache lediglich die Modelle ausgetauscht und sonst keinerlei \u00c4nderungen vorgenommen werden m\u00fcssen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"sentence-detector\">Sentence Detector<\/h2>\n\n\n\n<p>Der Sentence Detector splittet die Texteingabe bei jedem Punkt und erkennt, ob es sich dabei um ein Satzende handelt oder um einen Punkt in einer Datumsangabe oder Abk\u00fcrzung.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> openNLP = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"opennlp\"<\/span>);\n<span class=\"hljs-keyword\">var<\/span> sentence = <span class=\"hljs-string\">'Am 13. Juni 2014 wurde die deutsche Fu\u00dfball Nationalmannschaft '<\/span> + \n               <span class=\"hljs-string\">'Weltmeister. Das war der 4. Weltmeister Titel!'<\/span>;\n<span class=\"hljs-keyword\">var<\/span> sentenceDetector = <span class=\"hljs-keyword\">new<\/span> openNLP().sentenceDetector;\nsentenceDetector.sentDetect(sentence, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, results<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(results)\n    <span class=\"hljs-comment\">\/*\n    Ausgabe: \n    &#91; \n        'Am 13. Juni 2014 wurde die deutsche Fu\u00dfball  Nationalmannschaft Weltmeister.', \n        'Das war der 4. Weltmeister Titel!'\n    ]\n    *\/<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"tokenizer\">Tokenizer<\/h2>\n\n\n\n<p>Der Tokenizer splittet den Satz in einzelne W\u00f6rter und Satzzeichen auf. Leerzeichen zwischen Satzzeichen werden nicht verworfen, sondern als Token erkannt.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> openNLP = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"opennlp\"<\/span>);\n<span class=\"hljs-keyword\">var<\/span> sentences = <span class=\"hljs-string\">\"Am 12. Juni 2014 wurde die deutsche Fu\u00dfball Nationalmannschaft Weltmeister.\"<\/span>;\n<span class=\"hljs-keyword\">var<\/span> tokenizer = <span class=\"hljs-keyword\">new<\/span> openNLP().tokenizer;\ntokenizer.tokenize(sentence, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, results<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(results);\n    <span class=\"hljs-comment\">\/*\n    Ausgabe: &#91; \n                'Am',\n                '12',\n                '.',\n                'Juni',\n                '2014',\n                'wurde',\n                'die',\n                'deutsche',\n                'Fu\u00dfball',\n                'Nationalmannschaft',\n                'Weltmeister',\n                '.' \n            ]\n    *\/<\/span>\n})<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"optimierung\">Optimization<\/h2>\n\n\n\n<p>Verwendet man eine gro\u00dfe Datenmenge an Text, so ist der OpenNLP Wrapper sehr gut, um die Texte zu verarbeiten, jedoch arbeitet dieser nicht effizient, da die Tokenisierung sequentiell abl\u00e4uft und nicht parallel. Da die Dauer der Tokenisierung somit linear mit der Satzanzahl ansteigt, mussten wir hierf\u00fcr eine effiziente L\u00f6sung finden. Dazu haben wir uns einen eigenen minimalistischen OpenNLP Wrapper geschrieben, der den Sentence Detector und den Tokenizer beinhaltet. Dieser ist auf <a href=\"https:\/\/github.com\/flore2003\/opennlp-wrapper\" target=\"_self\">https:\/\/github.com\/flore2003\/opennlp-wrapper<\/a> zu finden und unterliegt der MIT License. Der Wrapper ist somit nicht vollkompatibel zum zuvor beschriebenen Wrapper.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"installation\">Installation<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">npm install opennlp-wrapper<\/code><\/span><\/pre>\n\n\n<p>Die Konfiguration, Sentence Splitting und Tokenisierung unterscheidet sich hierbei leicht vom originalen Projekt, sodass ein einfacher Tausch der Bibliothek nicht gen\u00fcgt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"konfiguration\">Konfiguration<\/h3>\n\n\n\n<p>Die folgende Konfiguration beschreibt den OpenNLP Wrapper, der von uns entwickelt wurde, mit den deutschen Modellen f\u00fcr den Sentence Detector und den Tokenizer initialisiert. Man beachte, dass auch hier die Konfiguration leicht angepasst wurde, im Vergleich zur original Bibliothek.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> openNLPOptions = {\n    <span class=\"hljs-attr\">models<\/span> : {\n        <span class=\"hljs-string\">\"tokenizer\"<\/span>: nlpPath +  <span class=\"hljs-string\">'de-token.bin'<\/span>,\n        <span class=\"hljs-string\">\"sentenceDetector\"<\/span>: nlpPath + <span class=\"hljs-string\">'de-sent.bin'<\/span>\n    },\n    <span class=\"hljs-string\">\"jarPath\"<\/span>: nlpPath +  <span class=\"hljs-string\">\"opennlp-tools-1.6.0.jar\"<\/span>\n};\n<span class=\"hljs-keyword\">var<\/span> openNLP = <span class=\"hljs-keyword\">new<\/span> OpenNLP(openNLPOptions);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"sentence-detector\">Sentence Detector<\/h3>\n\n\n\n<p class=\" translation-block\">In der originalen Bibliothek wurde auf der OpenNLP Instanz sentenceDetector.sentDetect() aufgerufen. Hier gen\u00fcgt lediglich der Methodenaufruf <strong>detectSentences()<\/strong> auf der OpenNLP Instanz.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> input = <span class=\"hljs-string\">'Am 12. Juni 2014 wurde die Fu\u00dfball Nationalmannschaft '<\/span> + \n            <span class=\"hljs-string\">'Fu\u00dfballweltmeister. Das war der 4. Weltmeister Titel!'<\/span>;\n\nopenNLP.detectSentences(input, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, sentences<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(sentences);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"tokenizer\">Tokenizer<\/h3>\n\n\n\n<p>In der originalen Bibliothek wurde auf der OpenNLP Instanz tokenizer.tokenize aufgerufen. Hier gen\u00fcgt lediglich der Methodenaufruf <strong>tokenize()<\/strong> auf der OpenNLP Instanz. <strong>tokenize()<\/strong> on the OpenNLP instance is sufficient.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> input = <span class=\"hljs-string\">'Am 12. Juni 2014 wurde die Fu\u00dfball Nationalmannschaft Fu\u00dfballweltmeister.'<\/span>;\n\nopenNLP.tokenize(input, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, tokens<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(tokens);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"anmerkungen-und-tipps\">Anmerkungen und Tipps<\/h2>\n\n\n\n<p>Damit die Daten f\u00fcr ein Machine Learning Verfahren, wie zum Beispiel Naive Bayes, geeignet sind, bedarf es noch weiteren Vorverarbeitungsschritten, wie die Verwendung eines Stemmers oder Lemmatisierers. F\u00fcr die Verarbeitung von deutschem Text haben wir einen Stemmer verwendet, der folgendes Problem l\u00f6st: Man betrachte die W\u00f6rter \u201eKauf, K\u00e4ufe, kaufen, kaufe\u201c, die sich alle auf den Erwerb eines Artikels beziehen, jedoch unterschiedlich verwendet werden, aber dennoch das Gleiche aussagen. Der Stemmer extrahiert aus jedem Wort den Wortstamm, was in diesem Fall \u201ekauf\u201c entspricht. F\u00fchrt man dieses Verfahren auf allen W\u00f6rtern durch, die man mittels des Tokenisierens ermittelt hat, so schrumpft die Anzahl unterschiedlicher W\u00f6rter um ein Vielfaches.<\/p>\n\n\n\n<p class=\" translation-block\">Der <a href=\"https:\/\/github.com\/hthetiot\/node-snowball\" data-type=\"URL\" data-id=\"https:\/\/github.com\/hthetiot\/node-snowball\" target=\"_self\">Snowball Stemmer<\/a> ist hierf\u00fcr sehr zu empfehlen, da er nicht nur sehr einfach zu verwenden ist, sondern auch \u00fcber 13 verschiedene Sprachen unterst\u00fctzt.<\/p>\n\n\n\n<p>Nachdem man alle W\u00f6rter gestemmt hat sollte man noch die Stopworte entfernen. Hierzu z\u00e4hlen im Deutschen unter anderem bestimmte Artikel, unbestimmte Artikel und Konjunktionen. Im Englischen sind die W\u00f6rter \u201aa\u2018, \u201aof\u2018, \u201athe\u2018, \u201aI\u2018, \u201ait\u2018, \u201ayou\u2018 und \u201aand\u2018 Teil der Stopw\u00f6rter. Eine sehr gute Variante Stopworte zu bestimmten, ist nach dem Stemming alle W\u00f6rter nach absteigender H\u00e4ufigkeit zu sortieren und die ersten 100 bis 200 W\u00f6rter dieser Liste zu betrachten und zu analysieren, welche W\u00f6rter wirklich nicht aussagekr\u00e4ftig sind und verworfen werden k\u00f6nnen.<\/p>\n\n\n\n<p>Im n\u00e4chsten Beitrag bauen wir auf diesem Wissen auf und entwickeln einen Naive Bayes Klassifizierer, der beispielsweise in der Spamerkennung eingesetzt wird.<\/p>","protected":false},"excerpt":{"rendered":"<p>The use of large data for machine learning procedures requires careful preprocessing, which is fundamental for the accuracy of the output. If we look at the classification of texts at word level, it is important that all texts are pre-processed, i.e. each text is divided into sentences and then into individual words. The OpenNLP Wrapper [&hellip;]<\/p>","protected":false},"author":4,"featured_media":2674,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_seopress_robots_primary_cat":"none","footnotes":""},"categories":[8],"tags":[],"class_list":["post-2672","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-machine-learning"],"acf":[],"_links":{"self":[{"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/posts\/2672","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/comments?post=2672"}],"version-history":[{"count":11,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/posts\/2672\/revisions"}],"predecessor-version":[{"id":5783,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/posts\/2672\/revisions\/5783"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/media\/2674"}],"wp:attachment":[{"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/media?parent=2672"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/categories?post=2672"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rocketloop.de\/de\/wp-json\/wp\/v2\/tags?post=2672"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}