[{"data":1,"prerenderedAt":650},["ShallowReactive",2],{"post-2019-07-16-mobilenet-in-nodejs":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"published":10,"summary":11,"draft":6,"image":12,"categories":13,"tags":15,"showAuthor":6,"authors":19,"showAuthorsBadges":6,"body":21,"_type":644,"_id":645,"_source":646,"_file":647,"_stem":648,"_extension":649},"/posts/2019-07-16-mobilenet-in-nodejs","posts",false,"","Development - MobileNet in NodeJS (Backend)","The origin of this blog post started when I wanted to be able to classify images on a backend server as a learning exercise, to be able to expand upon it for a project we are working on at my company (using a general object classifier as a baseline), and to also use computational power that will be needed further on.","2019-07-15T20:00:00-00:00","The origin of this blog post started when I wanted to be able to classify images on a backend server as a learning exercise.","/2019-07-16-mobilenet-in-nodejs.png",[14],"Technology",[16,17,18],"AI","TensorFlow","NodeJS",[20],"sean",{"type":22,"children":23,"toc":642},"root",[24,31,36,61,66,354,367,381,403,408,413,435,539,552,582,605,636],{"type":25,"tag":26,"props":27,"children":28},"element","p",{},[29],{"type":30,"value":9},"text",{"type":25,"tag":26,"props":32,"children":33},{},[34],{"type":30,"value":35},"Primarily as a NodeJS developer, I wished to complete the task in Node, knowing that TensorFlow.js had models which I could use, and that MobileNet was the most broad (being able to classify hundreds of objects, which I learned while using it for my Mobile Application Development Unit in University).",{"type":25,"tag":26,"props":37,"children":38},{},[39,41,50,52,59],{"type":30,"value":40},"Going through tensorflow on Github I found their ",{"type":25,"tag":42,"props":43,"children":47},"a",{"href":44,"rel":45},"https://github.com/tensorflow/tfjs-models",[46],"nofollow",[48],{"type":30,"value":49},"tfjs-models\n",{"type":30,"value":51},", a list of trained models where ",{"type":25,"tag":42,"props":53,"children":56},{"href":54,"rel":55},"https://github.com/tensorflow/tfjs-models/tree/master/mobilenet",[46],[57],{"type":30,"value":58},"MobileNet",{"type":30,"value":60}," was listed and where I started reading the documentation.",{"type":25,"tag":26,"props":62,"children":63},{},[64],{"type":30,"value":65},"In the readme, it had a section for Usage \"via NPM\" (shown below), which I thought implied on the backend, especially since \"via Script Tag\" has usage documentation (definitely for the front-end).",{"type":25,"tag":67,"props":68,"children":72},"pre",{"className":69,"code":70,"language":71,"meta":7,"style":7},"language-javascript shiki shiki-themes github-dark","// Note: you do not need to import @tensorflow/tfjs here.\n\nimport * as mobilenet from '@tensorflow-models/mobilenet';\n\nconst img = document.getElementById('img');\n\n// Load the model.\nconst model = await mobilenet.load();\n\n// Classify the image.\nconst predictions = await model.classify(img);\n\nconsole.log('Predictions: ');\nconsole.log(predictions);\n","javascript",[73],{"type":25,"tag":74,"props":75,"children":76},"code",{"__ignoreMap":7},[77,89,99,142,150,195,203,212,249,257,266,302,310,337],{"type":25,"tag":78,"props":79,"children":82},"span",{"class":80,"line":81},"line",1,[83],{"type":25,"tag":78,"props":84,"children":86},{"style":85},"--shiki-default:#6A737D",[87],{"type":30,"value":88},"// Note: you do not need to import @tensorflow/tfjs here.\n",{"type":25,"tag":78,"props":90,"children":92},{"class":80,"line":91},2,[93],{"type":25,"tag":78,"props":94,"children":96},{"emptyLinePlaceholder":95},true,[97],{"type":30,"value":98},"\n",{"type":25,"tag":78,"props":100,"children":102},{"class":80,"line":101},3,[103,109,115,120,126,131,137],{"type":25,"tag":78,"props":104,"children":106},{"style":105},"--shiki-default:#F97583",[107],{"type":30,"value":108},"import",{"type":25,"tag":78,"props":110,"children":112},{"style":111},"--shiki-default:#79B8FF",[113],{"type":30,"value":114}," *",{"type":25,"tag":78,"props":116,"children":117},{"style":105},[118],{"type":30,"value":119}," as",{"type":25,"tag":78,"props":121,"children":123},{"style":122},"--shiki-default:#E1E4E8",[124],{"type":30,"value":125}," mobilenet ",{"type":25,"tag":78,"props":127,"children":128},{"style":105},[129],{"type":30,"value":130},"from",{"type":25,"tag":78,"props":132,"children":134},{"style":133},"--shiki-default:#9ECBFF",[135],{"type":30,"value":136}," '@tensorflow-models/mobilenet'",{"type":25,"tag":78,"props":138,"children":139},{"style":122},[140],{"type":30,"value":141},";\n",{"type":25,"tag":78,"props":143,"children":145},{"class":80,"line":144},4,[146],{"type":25,"tag":78,"props":147,"children":148},{"emptyLinePlaceholder":95},[149],{"type":30,"value":98},{"type":25,"tag":78,"props":151,"children":153},{"class":80,"line":152},5,[154,159,164,169,174,180,185,190],{"type":25,"tag":78,"props":155,"children":156},{"style":105},[157],{"type":30,"value":158},"const",{"type":25,"tag":78,"props":160,"children":161},{"style":111},[162],{"type":30,"value":163}," img",{"type":25,"tag":78,"props":165,"children":166},{"style":105},[167],{"type":30,"value":168}," =",{"type":25,"tag":78,"props":170,"children":171},{"style":122},[172],{"type":30,"value":173}," document.",{"type":25,"tag":78,"props":175,"children":177},{"style":176},"--shiki-default:#B392F0",[178],{"type":30,"value":179},"getElementById",{"type":25,"tag":78,"props":181,"children":182},{"style":122},[183],{"type":30,"value":184},"(",{"type":25,"tag":78,"props":186,"children":187},{"style":133},[188],{"type":30,"value":189},"'img'",{"type":25,"tag":78,"props":191,"children":192},{"style":122},[193],{"type":30,"value":194},");\n",{"type":25,"tag":78,"props":196,"children":198},{"class":80,"line":197},6,[199],{"type":25,"tag":78,"props":200,"children":201},{"emptyLinePlaceholder":95},[202],{"type":30,"value":98},{"type":25,"tag":78,"props":204,"children":206},{"class":80,"line":205},7,[207],{"type":25,"tag":78,"props":208,"children":209},{"style":85},[210],{"type":30,"value":211},"// Load the model.\n",{"type":25,"tag":78,"props":213,"children":215},{"class":80,"line":214},8,[216,220,225,229,234,239,244],{"type":25,"tag":78,"props":217,"children":218},{"style":105},[219],{"type":30,"value":158},{"type":25,"tag":78,"props":221,"children":222},{"style":111},[223],{"type":30,"value":224}," model",{"type":25,"tag":78,"props":226,"children":227},{"style":105},[228],{"type":30,"value":168},{"type":25,"tag":78,"props":230,"children":231},{"style":105},[232],{"type":30,"value":233}," await",{"type":25,"tag":78,"props":235,"children":236},{"style":122},[237],{"type":30,"value":238}," mobilenet.",{"type":25,"tag":78,"props":240,"children":241},{"style":176},[242],{"type":30,"value":243},"load",{"type":25,"tag":78,"props":245,"children":246},{"style":122},[247],{"type":30,"value":248},"();\n",{"type":25,"tag":78,"props":250,"children":252},{"class":80,"line":251},9,[253],{"type":25,"tag":78,"props":254,"children":255},{"emptyLinePlaceholder":95},[256],{"type":30,"value":98},{"type":25,"tag":78,"props":258,"children":260},{"class":80,"line":259},10,[261],{"type":25,"tag":78,"props":262,"children":263},{"style":85},[264],{"type":30,"value":265},"// Classify the image.\n",{"type":25,"tag":78,"props":267,"children":269},{"class":80,"line":268},11,[270,274,279,283,287,292,297],{"type":25,"tag":78,"props":271,"children":272},{"style":105},[273],{"type":30,"value":158},{"type":25,"tag":78,"props":275,"children":276},{"style":111},[277],{"type":30,"value":278}," predictions",{"type":25,"tag":78,"props":280,"children":281},{"style":105},[282],{"type":30,"value":168},{"type":25,"tag":78,"props":284,"children":285},{"style":105},[286],{"type":30,"value":233},{"type":25,"tag":78,"props":288,"children":289},{"style":122},[290],{"type":30,"value":291}," model.",{"type":25,"tag":78,"props":293,"children":294},{"style":176},[295],{"type":30,"value":296},"classify",{"type":25,"tag":78,"props":298,"children":299},{"style":122},[300],{"type":30,"value":301},"(img);\n",{"type":25,"tag":78,"props":303,"children":305},{"class":80,"line":304},12,[306],{"type":25,"tag":78,"props":307,"children":308},{"emptyLinePlaceholder":95},[309],{"type":30,"value":98},{"type":25,"tag":78,"props":311,"children":313},{"class":80,"line":312},13,[314,319,324,328,333],{"type":25,"tag":78,"props":315,"children":316},{"style":122},[317],{"type":30,"value":318},"console.",{"type":25,"tag":78,"props":320,"children":321},{"style":176},[322],{"type":30,"value":323},"log",{"type":25,"tag":78,"props":325,"children":326},{"style":122},[327],{"type":30,"value":184},{"type":25,"tag":78,"props":329,"children":330},{"style":133},[331],{"type":30,"value":332},"'Predictions: '",{"type":25,"tag":78,"props":334,"children":335},{"style":122},[336],{"type":30,"value":194},{"type":25,"tag":78,"props":338,"children":340},{"class":80,"line":339},14,[341,345,349],{"type":25,"tag":78,"props":342,"children":343},{"style":122},[344],{"type":30,"value":318},{"type":25,"tag":78,"props":346,"children":347},{"style":176},[348],{"type":30,"value":323},{"type":25,"tag":78,"props":350,"children":351},{"style":122},[352],{"type":30,"value":353},"(predictions);\n",{"type":25,"tag":26,"props":355,"children":356},{},[357,359,365],{"type":30,"value":358},"The first thing that seemed odd was the use of ",{"type":25,"tag":74,"props":360,"children":362},{"className":361},[],[363],{"type":30,"value":364},"document.getElementById('img')",{"type":30,"value":366},", which is a front-end concept (the document being the webpage you are on), which led me to try to find if anyone else has tried this task before.",{"type":25,"tag":26,"props":368,"children":369},{},[370,372,379],{"type":30,"value":371},"Fortunately for me, a developer called James Thomas wrote a blog post back in 2018 called ",{"type":25,"tag":42,"props":373,"children":376},{"href":374,"rel":375},"http://jamesthom.as/blog/2018/08/07/machine-learning-in-node-dot-js-with-tensorflow-dot-js/",[46],[377],{"type":30,"value":378},"Machine Learning in Node.js With TensorFlow.js",{"type":30,"value":380},", which gave useful advice, it explained why elements would not work in NodeJS (as I found prior to attempting to run the demo code), how to fix these issues and gave code snippets (which I started experimenting with).",{"type":25,"tag":26,"props":382,"children":383},{},[384,386,392,394,401],{"type":30,"value":385},"After trying to run their code, I found that it did not even get past ",{"type":25,"tag":74,"props":387,"children":389},{"className":388},[],[390],{"type":30,"value":391},"npm install",{"type":30,"value":393},", combing through the console logs, I found that TensorFlow.js was trying to be complied through ",{"type":25,"tag":42,"props":395,"children":398},{"href":396,"rel":397},"https://github.com/nodejs/node-gyp",[46],[399],{"type":30,"value":400},"node-gyp",{"type":30,"value":402},", which I did not have set up on my machine.",{"type":25,"tag":26,"props":404,"children":405},{},[406],{"type":30,"value":407},"The installation process was not too complex, it simply requires installing the package globally and installing the windows build tools (specifically on Windows, via npm).",{"type":25,"tag":26,"props":409,"children":410},{},[411],{"type":30,"value":412},"After I managed to get the needed packages installed, I found myself running into more issues trying to try the demo, it seemed as if it did not like to load MobileNet, creating a new instance yielded results through console logs, but beyond that it broke.",{"type":25,"tag":26,"props":414,"children":415},{},[416,418,424,426,433],{"type":30,"value":417},"After a bit of experimentation, I removed the line containing ",{"type":25,"tag":74,"props":419,"children":421},{"className":420},[],[422],{"type":30,"value":423},"mn.path = `file://${path}` ",{"type":30,"value":425},", since it the mn object already had a path linking to google, then after searching for a related error in the console log, I found an ",{"type":25,"tag":42,"props":427,"children":430},{"href":428,"rel":429},"https://github.com/tensorflow/tfjs/issues/740#issuecomment-427895896",[46],[431],{"type":30,"value":432},"issues thread",{"type":30,"value":434}," on tfjs, which recommended modifying the code so it was as follows:",{"type":25,"tag":67,"props":436,"children":438},{"className":69,"code":437,"language":71,"meta":7,"style":7},"const mobilenet = require('@tensorflow-models/mobilenet')\nglobal.fetch = require('node-fetch')\nconst model = await mobilenet.load()\n",[439],{"type":25,"tag":74,"props":440,"children":441},{"__ignoreMap":7},[442,477,507],{"type":25,"tag":78,"props":443,"children":444},{"class":80,"line":81},[445,449,454,458,463,467,472],{"type":25,"tag":78,"props":446,"children":447},{"style":105},[448],{"type":30,"value":158},{"type":25,"tag":78,"props":450,"children":451},{"style":111},[452],{"type":30,"value":453}," mobilenet",{"type":25,"tag":78,"props":455,"children":456},{"style":105},[457],{"type":30,"value":168},{"type":25,"tag":78,"props":459,"children":460},{"style":176},[461],{"type":30,"value":462}," require",{"type":25,"tag":78,"props":464,"children":465},{"style":122},[466],{"type":30,"value":184},{"type":25,"tag":78,"props":468,"children":469},{"style":133},[470],{"type":30,"value":471},"'@tensorflow-models/mobilenet'",{"type":25,"tag":78,"props":473,"children":474},{"style":122},[475],{"type":30,"value":476},")\n",{"type":25,"tag":78,"props":478,"children":479},{"class":80,"line":91},[480,485,490,494,498,503],{"type":25,"tag":78,"props":481,"children":482},{"style":122},[483],{"type":30,"value":484},"global.fetch ",{"type":25,"tag":78,"props":486,"children":487},{"style":105},[488],{"type":30,"value":489},"=",{"type":25,"tag":78,"props":491,"children":492},{"style":176},[493],{"type":30,"value":462},{"type":25,"tag":78,"props":495,"children":496},{"style":122},[497],{"type":30,"value":184},{"type":25,"tag":78,"props":499,"children":500},{"style":133},[501],{"type":30,"value":502},"'node-fetch'",{"type":25,"tag":78,"props":504,"children":505},{"style":122},[506],{"type":30,"value":476},{"type":25,"tag":78,"props":508,"children":509},{"class":80,"line":101},[510,514,518,522,526,530,534],{"type":25,"tag":78,"props":511,"children":512},{"style":105},[513],{"type":30,"value":158},{"type":25,"tag":78,"props":515,"children":516},{"style":111},[517],{"type":30,"value":224},{"type":25,"tag":78,"props":519,"children":520},{"style":105},[521],{"type":30,"value":168},{"type":25,"tag":78,"props":523,"children":524},{"style":105},[525],{"type":30,"value":233},{"type":25,"tag":78,"props":527,"children":528},{"style":122},[529],{"type":30,"value":238},{"type":25,"tag":78,"props":531,"children":532},{"style":176},[533],{"type":30,"value":243},{"type":25,"tag":78,"props":535,"children":536},{"style":122},[537],{"type":30,"value":538},"()\n",{"type":25,"tag":26,"props":540,"children":541},{},[542,544,550],{"type":30,"value":543},"The modification was the overwrite of the global fetch, although this still left one last error, a different one from last time, being ",{"type":25,"tag":74,"props":545,"children":547},{"className":546},[],[548],{"type":30,"value":549},"Error: Unknown feature TENSORLIKE_CHECK_SHAPE_CONSISTENCY.",{"type":30,"value":551},".",{"type":25,"tag":26,"props":553,"children":554},{},[555,557,564,566,572,574,580],{"type":30,"value":556},"A quick Google of this error lead to the same thread and a ",{"type":25,"tag":42,"props":558,"children":561},{"href":559,"rel":560},"https://github.com/tensorflow/tfjs/issues/740#issuecomment-432983629",[46],[562],{"type":30,"value":563},"different comment",{"type":30,"value":565}," further down, which stated the same modifications were made as above (modifying the ",{"type":25,"tag":74,"props":567,"children":569},{"className":568},[],[570],{"type":30,"value":571},"ms.path",{"type":30,"value":573}," and ",{"type":25,"tag":74,"props":575,"children":577},{"className":576},[],[578],{"type":30,"value":579},"global.fetch",{"type":30,"value":581},"), but then made the following modification:",{"type":25,"tag":583,"props":584,"children":585},"blockquote",{},[586],{"type":25,"tag":26,"props":587,"children":588},{},[589,591,597,603],{"type":30,"value":590},"And then , I bump tfjs to the latest release:\n",{"type":25,"tag":74,"props":592,"children":594},{"className":593},[],[595],{"type":30,"value":596},"\"@tensorflow/tfjs\": \"^0.13.0\",",{"type":25,"tag":74,"props":598,"children":600},{"className":599},[],[601],{"type":30,"value":602},"\"@tensorflow/tfjs-node\": \"^0.1.19\",",{"type":30,"value":604},"\nIt works for me now.",{"type":25,"tag":26,"props":606,"children":607},{},[608,610,616,618,625,627,634],{"type":30,"value":609},"Now when I ran ",{"type":25,"tag":74,"props":611,"children":613},{"className":612},[],[614],{"type":30,"value":615},"node script.js mobilenet/model.json image.jpg",{"type":30,"value":617}," through the command line, it finally yielded a prediction, due to the modification I had to make, and the code only originally being in a ",{"type":25,"tag":42,"props":619,"children":622},{"href":620,"rel":621},"https://gist.github.com/jthomas/145610bdeda2638d94fab9a397eb1f1d",[46],[623],{"type":30,"value":624},"GitHub Gist",{"type":30,"value":626},", I thought I would publish it as a ",{"type":25,"tag":42,"props":628,"children":631},{"href":629,"rel":630},"https://github.com/Sean12697/MobileNet-via-TensorFlowJS-in-NodeJS",[46],[632],{"type":30,"value":633},"GitHub Project",{"type":30,"value":635},", with credit to the original author and a link back to this blog post (to explain why it exists).",{"type":25,"tag":637,"props":638,"children":639},"style",{},[640],{"type":30,"value":641},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":91,"depth":91,"links":643},[],"markdown","content:posts:2019-07-16-mobilenet-in-nodejs.md","content","posts/2019-07-16-mobilenet-in-nodejs.md","posts/2019-07-16-mobilenet-in-nodejs","md",1779024902883]