{"id":21,"date":"2016-11-09T03:23:36","date_gmt":"2016-11-09T03:23:36","guid":{"rendered":"http:\/\/freedville.com\/blog\/?p=21"},"modified":"2019-04-12T14:14:20","modified_gmt":"2019-04-12T14:14:20","slug":"training-a-personal-chatbot-with-watson-developer-cloud-apis","status":"publish","type":"post","link":"https:\/\/freedville.com\/blog\/2016\/11\/09\/training-a-personal-chatbot-with-watson-developer-cloud-apis\/","title":{"rendered":"Training a personal chatbot with Watson Developer Cloud APIs"},"content":{"rendered":"<p>The <a href=\"http:\/\/www.ibm.com\/watson\/developercloud\/\">Watson Developer Cloud APIs<\/a> are intended to give you building blocks for building your own applications with the cognitive capabilities of Watson. &nbsp;There&#8217;s been a lot of press on <a href=\"https:\/\/www.ibm.com\/blogs\/watson\/2016\/10\/easy-slack-integration-watson-conversation\/\">combining Watson and Slack<\/a>, and I&#8217;d like to give my own views on how one should get started with the training process.<\/p>\n<p><strong>What is classification?<\/strong><\/p>\n<p>The key technology behind the <a href=\"https:\/\/www.ibm.com\/watson\/developercloud\/conversation.html\">Conversation<\/a>&nbsp;and <a href=\"https:\/\/www.ibm.com\/watson\/developercloud\/nl-classifier.html\">Natural Language Classifier<\/a> services&nbsp;is the use of classification. &nbsp;For text classification, the core question boils down to &#8220;given a block of text, is it more likely an&nbsp;<em>X<\/em> or <em>Y<\/em>?&#8221; or &#8220;given a block of text, should I classify it as an <em>X<\/em> or <em>Y<\/em>? The classifier is trained by selecting examples of text that mean <em>X<\/em> and examples that mean <em>Y<\/em>. &nbsp;After the classifier is trained, it can make classification decisions on blocks of text it has never seen before.<\/p>\n<p><strong>Finding training data for the classifier behind a chat bot<\/strong><\/p>\n<p>That&#8217;s all well and good in the abstract. &nbsp;But how do we put it to use? &nbsp;I&#8217;m going to describe how to build a personal chat bot. &nbsp;I use an instant messaging program at work &#8211; it would be great if a chat bot could screen my incoming messages and respond for me, at least for the easy stuff. &nbsp;To do this, I need to gather a significant amount of training data and train some classifiers.<\/p>\n<p>Fortunately, my instant messaging application logs all chat transcripts. &nbsp;700MB of chat transcripts, in fact! &nbsp;I first narrowed down the transcripts by searching for all messages containing a question mark. &nbsp;This yields over 66,000 questions. &nbsp;Scanning the questions gives me a rough idea of what kinds of questions I am being asked, but building a chat bot still feels overwhelming. &nbsp;Now what?<\/p>\n<p>Searching for training data:<br \/>\n<code>grep -R \\? SametimeTranscripts &gt; ..\/SametimeQuestions.html<br \/>\ncat ..\/SametimeQuestions.html | cut -d '&gt;' -f 12 | cut -d '&lt;' -f 1 | sort | uniq &gt; ..\/SametimeQuestions.txt<\/code><\/p>\n<p>First, a major thing I did, and another that I did NOT do. &nbsp;The thing I did was to narrow my aim. &nbsp;Instead of building an overall chat bot, I narrowed in on a lunch-assisting chat bot. &nbsp;The bot&#8217;s only job is to determine if a question is about lunch (my answer will be &#8220;I&#8217;d love to go, pick me up at 11:30&#8221;) or about anything else (in which case, don&#8217;t answer for me). &nbsp;The thing I did NOT do was give up on my chat transcripts and manually create questions. &nbsp;This is important, because classifiers work best if they are trained on representative data. &nbsp;It would take less effort to create &#8216;synthetic&#8217; questions, but the classifier performance would suffer (more on this later).<\/p>\n<p>Focusing on a simple classification scheme (&#8220;lunch&#8221; or &#8220;other&#8221;) made it easier to find representative questions.<\/p>\n<p>For &#8220;lunch&#8221;, I used simple &#8216;grep&#8217; commands to find the word &#8216;lunch&#8217; or restaurants I commonly go to. &nbsp;Examples:<br \/>\n<code>grep -i lunch SametimeQuestions.txt<br \/>\ngrep -i carmens SametimeQuestions.txt<\/code><\/p>\n<p>For &#8220;other&#8221;, I simply grabbed a random sampling of questions (<a href=\"http:\/\/linuxcommando.blogspot.com\/2008\/04\/use-sed-or-perl-to-extract-every-nth.html\">Perl script reference<\/a>)<br \/>\n<code>perl -ne 'print ((0 == $. % 100) ? $_ : \"\")' SametimeQuestions.txt<\/code><\/p>\n<p>For a starter set, I copied 20+&nbsp;questions from both command sets into a single Excel file. &nbsp;Column A for the question, Column B for classification (&#8216;lunch&#8217; or &#8216;other&#8217;).<\/p>\n<p>Sample training data:<br \/>\n<code>lunch @ 1130?,lunch<br \/>\ncarmen\u2019s?,lunch<br \/>\nmoes?,lunch<br \/>\nCan I call?,other<br \/>\nDo I need to assign a change set in order to check in?,other<br \/>\nAndrew- u have extra shinguard?,other<br \/>\nlet's chat after I grab some lunch .. you have some time this afternoon?,other<\/code><\/p>\n<p>Full training data:&nbsp;<a href=\"http:\/\/freedville.com\/blog\/wp-content\/uploads\/2016\/11\/AndrewChatsNLCTraining.csv\">Lunch vs Other NLC Training Data<\/a><\/p>\n<p>The command which produced the question is a strong indicator of the classification I should use, but it&#8217;s worth noting this is not 100% reliable. &nbsp;For instance, one of my questions was &#8220;let&#8217;s chat after I grab some lunch .. you have some time this afternoon?&#8221; &#8211; this is clearly not a lunch invitation but rather a desire to have a meeting, so I had to pay attention while classifying it. &nbsp;After saving this file as a CSV, I was ready to upload it to the Watson APIs as training data.<\/p>\n<p>Protip: Double-check your file contents here. &nbsp;String-escape your questions (or remove the commas), so that your training file is treated as a two-column CSV. &nbsp;Beware also of special characters like &#8220;smart quotes&#8221;, which will be rejected by the training API. &nbsp;(Fortunately, the API will tell you the line number of any special characters it doesn&#8217;t understand.)<\/p>\n<p><strong>Sending training data to an NLC classifier service<\/strong><\/p>\n<p>I used NLC API instead of going right to Conversation since I want to focus on the training techniques and I find NLC slightly easier to get started with. &nbsp;I followed the NLC <a href=\"https:\/\/www.ibm.com\/watson\/developercloud\/doc\/nl-classifier\/get_start.shtml\">Getting Started Reference<\/a>&nbsp;and created a service instance at&nbsp;<a href=\"https:\/\/console.ng.bluemix.net\/catalog\/services\/natural-language-classifier\/\">https:\/\/console.ng.bluemix.net\/catalog\/services\/natural-language-classifier\/<\/a>. &nbsp;Clicking the Credentials tab gives me the user, password, and URL I need to get started. &nbsp;(The <a href=\"http:\/\/www.ibm.com\/watson\/developercloud\/natural-language-classifier\/api\/v1\/\">NLC API Reference<\/a> is a great cookbook to use once you have these three things.)<\/p>\n<p>Training my first classifier:<br \/>\n<code>curl -u \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\":\"xxxxxxxxxxxx\" -F training_data=@AndrewChatsNLCTraining.csv -F training_metadata=\"{\\\"language\\\":\\\"en\\\",\\\"name\\\":\"ARF-Chatbot-NLC\\\"}\" https:\/\/gateway.watsonplatform.net\/natural-language-classifier\/api\/v1\/classifiers<br \/>\n{<br \/>\n\"classifier_id\" : \"xxxxxxxxxx-nlc-xxxx\",<br \/>\n\"name\" : \"ARF-Chatbot-NLC\",<br \/>\n\"language\" : \"en\",<br \/>\n\"created\" : \"2016-11-07T03:27:26.678Z\",<br \/>\n\"url\" : \"https:\/\/gateway.watsonplatform.net\/natural-language-classifier\/api\/v1\/classifiers\/xxxxxxxxxx-nlc-xxxx\",<br \/>\n\"status\" : \"Training\",<br \/>\n\"status_description\" : \"The classifier instance is in its training phase, not yet ready to accept classify requests\"<br \/>\n}<\/code><\/p>\n<p>Protip: Make sure your curl command doesn&#8217;t have smart-quotes either, or the command won&#8217;t be recognized! (Several times curl gave me an error that a URL was not in the command, due to smart quotes)<\/p>\n<p>A few minutes later, I checked the status and the classifier was ready to sort text into &#8220;lunch&#8221; or &#8220;other&#8221; categories.<\/p>\n<p><strong>Testing the classifer<\/strong><\/p>\n<p>My first question was close to the training set:<br \/>\n<code>curl -G -u \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\":\"xxxxxxxxxxxx\" https:\/\/gateway.watsonplatform.net\/natural-language-classifier\/api\/v1\/classifiers\/xxxxxxxxxx-nlc-xxxx\/classify --data-urlencode \"text=lunch @ 1145?\"<\/code><\/p>\n<p>with high confidence results:<br \/>\n<code>{<br \/>\n\"classifier_id\" : \"xxxxxxxxxx-nlc-xxxx\",<br \/>\n\"url\" : \"https:\/\/gateway.watsonplatform.net\/natural-language-classifier\/api\/v1\/classifiers\/xxxxxxxxxx-nlc-xxxx\",<br \/>\n\"text\" : \"lunch @ 1145?\",<br \/>\n\"top_class\" : \"lunch\",<br \/>\n\"classes\" : [ {<br \/>\n\"class_name\" : \"lunch\",<br \/>\n\"confidence\" : 0.9923420767749419<br \/>\n}, {<br \/>\n\"class_name\" : \"other\",<br \/>\n\"confidence\" : 0.007657923225058088<br \/>\n} ]<br \/>\n}<\/code><\/p>\n<p>The training process yielded some other interesting results:<\/p>\n\n<table id=\"tablepress-3\" class=\"tablepress tablepress-id-3\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Question<\/th><th class=\"column-2\">Classification<\/th><th class=\"column-3\">Confidence<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"row-2\">\n\t<td class=\"column-1\">sure, when do you need it by?<\/td><td class=\"column-2\">other<\/td><td class=\"column-3\">99.4%<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">talk after lunch?<\/td><td class=\"column-2\">lunch<\/td><td class=\"column-3\">99.0%<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">can we talk after your lunch?<\/td><td class=\"column-2\">lunch<\/td><td class=\"column-3\">79.9%<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">o'charleys?<\/td><td class=\"column-2\">lunch<\/td><td class=\"column-3\">95.1%<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">production?<\/td><td class=\"column-2\">lunch<\/td><td class=\"column-3\">94.9%<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">How much wood would a woodchuck chuck if a woodchuck could chuck wood?<\/td><td class=\"column-2\">other<\/td><td class=\"column-3\">80.7%<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-3 from cache -->\n<p>The classifier gets some questions right with very high confidence, and some very wrong as well. &nbsp;It has extracted patterns from the training data. &nbsp;My training data shows that a short question, or a question that uses the word lunch, is very likely to be a lunch question. &nbsp;This was good for O&#8217;Charleys (a place I have not gone for lunch), or the 11:45 question (too late for me to go to lunch!), but bad for the quick question about our production environment (who can go to lunch if production is down?)<\/p>\n<p><strong>How to improve the classifier<\/strong><\/p>\n<p>The solution will be to use more training data. &nbsp;50 is the bare minimum number of cases to train a classifier, I should really have tried to collect 100 examples for each classification. &nbsp;Additionally I should check that I have accurately sampled my transcripts (do I have questions about lunch with more words? shorter questions not about lunch? non-lunch questions that contain the word lunch?)<\/p>\n<p>It&#8217;s worth reiterating here the need to use representative training data rather than trying to create it synthetically. &nbsp;If I was creating training data manually, I would have had a bunch of questions like &#8220;Hey Andrew, do you want to eat lunch with us today?&#8221; &nbsp;Not only is that far better punctuation than I see in a typical chat transcript, it would provide other distracting or misleading signals to the classifier. &nbsp;In my chat transcripts, the word &#8220;eat&#8221; appears in only 3 out of 66,000 questions, and none were in invitations to lunch!<\/p>\n<p><strong>Conclusion<\/strong><\/p>\n<p>While the use of a lunch concierge chat bot is a bit tongue in cheek, I hope this shows you&nbsp;how to get started with text classification. &nbsp;It&#8217;s important to use training data from reality, not your imagination, and to use a representative sample of that real data.<\/p>\n<p>Click through to <a href=\"http:\/\/freedville.com\/blog\/2016\/11\/12\/training-a-personal-chatbot-with-watson-developer-cloud-apis-part-2\/\">part 2<\/a> where I explore more complicated classification scenarios, including classifying against multiple dimensions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Watson Developer Cloud APIs are intended to give you building blocks for building your own applications with the cognitive capabilities of Watson. &nbsp;There&#8217;s been a lot of press on combining Watson and Slack, and&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[9,5,4],"_links":{"self":[{"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/posts\/21"}],"collection":[{"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/comments?post=21"}],"version-history":[{"count":10,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/posts\/21\/revisions"}],"predecessor-version":[{"id":427,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/posts\/21\/revisions\/427"}],"wp:attachment":[{"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/media?parent=21"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/categories?post=21"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/freedville.com\/blog\/wp-json\/wp\/v2\/tags?post=21"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}