-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.xml
1111 lines (900 loc) · 92.1 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>GopherData on GopherData</title>
<link>http://gopherdata.io/</link>
<description>Recent content in GopherData on GopherData</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<copyright>&copy; 2017 GopherData</copyright>
<lastBuildDate>Wed, 20 Apr 2016 00:00:00 +0000</lastBuildDate>
<atom:link href="/" rel="self" type="application/rss+xml" />
<item>
<title>GopherData Conference</title>
<link>http://gopherdata.io/talk/gopherdata/</link>
<pubDate>Wed, 01 Jan 2042 00:00:00 +0000</pubDate>
<guid>http://gopherdata.io/talk/gopherdata/</guid>
<description><p>More details, actual dates (hopefully sooner than 2042), and details coming soon!</p>
</description>
</item>
<item>
<title>More Go-based Workflow Tools in Bioinformatics</title>
<link>http://gopherdata.io/post/more_go_based_workflow_tools_in_bioinformatics/</link>
<pubDate>Thu, 02 Nov 2017 23:10:02 +0100</pubDate>
<guid>http://gopherdata.io/post/more_go_based_workflow_tools_in_bioinformatics/</guid>
<description>
<p>It is an exciting time for Go as a data science language and for the
<a href="http://gopherdata.io/" target="_blank">#gopherdata</a> movement. The
<a href="https://github.com/gopherdata/resources/blob/master/tooling/README.md" target="_blank">ecosystem of tools</a>
is constantly improving, with both general purpose tools (e.g., for data frames and
statistical analyses) and more specialized ones (e.g., for neural networks and
graph-based algorithms) popping up every day.</p>
<p>Recent weeks have been particularly exciting for those involved in the
bioinformatics field. In addition to generic libraries for bioinformatics such
as <a href="https://github.com/biogo/biogo" target="_blank">bíogo</a>, which was recently reviewed in quite some detail in a two blog posts
(<a href="https://medium.com/@boti_ka/a-gentle-introduction-to-b%C3%ADogo-part-i-65dbd40e31d4" target="_blank">part I</a>
and <a href="https://medium.com/@boti_ka/a-gentle-introduction-to-bíogo-part-ii-1f0df1cf72f0" target="_blank">part II</a>),
the ecosystem of scientific workflow tools focusing on or being used in
bioinformatics is also growing: Last week, another Go-based workflow
orchestration tool, <a href="https://github.com/grailbio/reflow" target="_blank">Reflow</a>, was released
as open source, by life science startup <a href="https://grail.com/" target="_blank">Grail Inc</a>.</p>
<p>Reflow brings a number of interesting features to the
table including: (i)&nbsp;comprehensive and very easy-to-use Amazon Web
Services (AWS) integration, (ii)&nbsp;memoization features based on S3
storage, and (iii)&nbsp;ability to run the same, docker-based workflow on your
local computer or on AWS.</p>
<p>Because we are users of and contributors to two other existing Go-based
workflow projects (<a href="http://pachyderm.io/" target="_blank">Pachyderm</a> and
<a href="http://scipipe.org/" target="_blank">SciPipe</a>), we thought that a brief comparison of the
different approaches would be useful. In particular, we hope that this summary
helps highlight differences between the tools, guide users to appropriate
workflow and orchestration tools, and/or provide a jumping-off-point for
contributions and experimentation</p>
<h2 id="workflow-tools-in-bio-and-cheminformatics">Workflow Tools in Bio- and Cheminformatics</h2>
<p>Before we continue, lets step back and add a few words about what workflow
tools are and why they are relevant in Bioinformatics (or Science in general),
for anyone new to the term.</p>
<p><a href="https://en.wikipedia.org/wiki/Scientific_workflow_system" target="_blank">Scientific workflow tools</a> are tools or
systems designed to help coordinate computations when the number of computation
steps is very large and/or the steps have complex dependencies between their
input and output data. This is common in many scientific fields, but it is
common in bioinformatics in particular, because of the vast plethora of tools
built to analyze the extremely heterogeneous data types describing biological
organisms, from molecules to cells to organs to full body physiology.</p>
<p>Common tasks in bioinformatics necessitating the use of workflow tools include
(DNA) sequence alignment, (gene) variant calling, RNA quantification, and more.
In the related field of cheminformatics, workflow tools are often used to build
predictive models relating the chemical structure of drug compounds to some
measurable property of the compound, in what is commonly called Quantitative
Structure Activity Relationship (QSAR). Examples of such properties are the
level of binding to protein targets in the body, or various chemical properties
such as solubility, that might affect its potential for update in the body.</p>
<h2 id="the-go-bioinformatics-workflow-tooling-ecosystem">The Go bioinformatics workflow tooling ecosystem</h2>
<p><img src="http://gopherdata.io/img/wftools/gopher_thinking_workflows.png" alt="Gopher thinking about workflows, surrounded by workflow tool logos" /></p>
<p>The main Go-based workflow tools that have targeted bioinformatics workflows
(or have been used to implement bioinformatics workflows) are:</p>
<p><strong><a href="http://pachyderm.io/" target="_blank">Pachyderm</a></strong> - This platform allows users to build
scalable, containerized data pipelines using any language or framework, while
also getting the right data to the right code as both data and code change over
time. Individual pipeline stages are defined by docker images and can be
individually parallelized across large data sets (by leveraging
<a href="https://kubernetes.io/" target="_blank">Kubernetes</a> under the hood).</p>
<p><strong><a href="http://scipipe.org/" target="_blank">SciPipe</a></strong> - This tool is much smaller than something
like Pachyderm and is focused on highly dynamic workflow constructs in
which on-line scheduling is key. SciPipe also focuses, thus far, on workflows
that run on HPC clusters and local computers. Although, it can be integrated
in a more comprehensive framework, such as Pachyderm or Reflow.</p>
<p><strong><a href="https://github.com/MG-RAST/AWE" target="_blank">AWE</a></strong> - The AWE framework is a
comprehensive bioinformatics system targeting the cloud. It reportedly
comes with multi-cloud support and, as it seems, HPC support, and AWE allows
users to leverage the <a href="http://www.commonwl.org/" target="_blank">Common Workflow Language (CWL)</a>. As
with Reflow, we don’t have hands-on experience with AWE, so our comments
are limited to what we’ve read in the documentation and examples.</p>
<h2 id="comparing-reflow-and-pachyderm">Comparing Reflow and Pachyderm</h2>
<p>Reflow and Pachyderm seems to be closest at first glance. Both frameworks
utilize containers as the main unit of data processing. However, there are some
differences in both workflow orchestration and data storage/management that we
will stress below.</p>
<h3 id="orchestration">Orchestration</h3>
<p>While Reflow implements custom “runners” for each target environment (AWS and
your local machine currently), Pachyderm leverages Kubernetes under-the-hood
for container orchestration. This use of Kubernetes allows Pachyderm to
be maximally vendor-agnostic, as Kubernetes is widely deployed in all the
major clouds and on-premise. Reflow authors are reportedly planning to
support other cloud providers, like GCP, in the near future though, but
this effort seems to require writing custom runner code per target
environment/cloud.</p>
<p>The use of Kubernetes also allows Pachyderm to automatically bring certain
orchestration functionality into a context that has otherwise
propelled Kubernetes to become industry leader in orchestration. More
specifically, Pachyderm pipelines are automatically “self-healing” in that they are
rescheduled and restarted when cluster nodes or jobs fail, and Pachyderm is
able to optimally schedule work across high-value nodes (e.g., high-CPU or GPU
nodes) using methods such as auto-scaling. Reflow appears to use it’s own
logic to match workloads with available instance types.</p>
<p>Finally, Pachyderm and Reflow have differences in the way workflows are
actually defined. Pachyderm leverages the established JSON/YAML format that is
common in the Kubernetes community, and Reflow implements what seems to be its
own readable, Go-inspired domain specific language (DSL).</p>
<h3 id="data-storage-management">Data Storage/Management</h3>
<p>For the data that is to be processed in workflow stages, Reflow provides an
interesting memoization mechanism based on S3, with automatic re-runs triggered
by updates to data. Similarly, Pachyderm provides a familiar, git-inspired
versioned data store, where new versions of data in the versioned data store
are also used to automatically trigger relevant parts of the workflow.
Pachyderm’s data store can be backed by any of the popular vendor-specific
object stores (GCS in GCP, S3 in AWS, or Blob Storage in Azure) or any other
object store with an S3-compatible API (e.g., an open source option like
Minio).</p>
<p>In terms of metadata associated with jobs, versions of data, etc., Reflow
appears to use a Reflow-specific cache based on S3 and DynamoDB. In
contrast, Pachyderm utilizes etcd, a distributed key-value store, for metadata.</p>
<p>Lastly with regard to data sharding and parallelization, Pachyderm seems to go
further than Reflow. While Reflow does parallelize tools running on separate
data sets, Pachyderm also provides automatic parallelization of tools accessing
“datums” that may correspond to the same data set or even the same
file. Pachyderm automatically distributes the processing of these datums to
containers running in parallel (pods in Kubernetes) and gathers all of the
results back into a single logical collection of data. Pachyderm thus provides
what other frameworks like Hadoop and Spark are promising, but without the need
to replace legacy code and tools with code written in the MapReduce-style or
explicitly implement parallelism and data sharding in code.</p>
<p>With these notes in mind, we think Reflow and Pachyderm are addressing slightly
different user needs. While Reflow seems to be an excellent choice for a quick
setup on AWS or a local docker-based server, we think Pachyderm will generally
provide more vendor agnosticity, better parallelization, and valuable
optimizations and updates that come out of the growing
Kubernetes community. Finally, we think that Pachyderm provides a stronger
foundation for rigorous and manageable data science with its unified data
versioning system, which can help data scientists and engineers better
understand data, collaborate, perform tests, share workflows, and so on.</p>
<h2 id="how-does-scipipe-compare">How does SciPipe compare?</h2>
<p>SciPipe is, in this context, more of an apples-to-oranges comparison to
Pachyderm, Reflow or AWE. While Reflow and Pachyderm provides an integrated
tool encapsulation solution based on containers, SciPipe (in its current
form) is primarily focused on managing command-line driven workflows on local
computers or HPC clusters with a shared file system, where containers might not
be an option. However, there are also other relevant differences related to
complexity of the tools, workflow implementation, and deployment/integration:</p>
<h3 id="complexity">Complexity</h3>
<p>SciPipe is a much smaller tool in many ways. For example, a very simple
count of LOC for the different frameworks shows that SciPipe is implemented with
more than an order of magnitude less lines of code than the other tools:</p>
<pre><code class="language-bash">$ cd $GOPATH/src/github.com/grailbio/reflow
$ find | grep &quot;\.go&quot; | grep -vP “(vendor|examples|_test)” | xargs cat | grep -vP &quot;^\/\/&quot; | sed '/^\s*$/d' | wc -l
26371
</code></pre>
<pre><code class="language-bash">$ cd $GOPATH/src/github.com/pachyderm/pachyderm/src
$ find | grep &quot;\.go&quot; | grep -vP “(vendor|examples|_test)” | xargs cat | grep -vP &quot;^\/\/&quot; | sed '/^\s*$/d' | wc -l
25778
</code></pre>
<pre><code class="language-bash">$ cd $GOPATH/src/github.com/MG-RAST/AWE
$ find | grep &quot;\.go&quot; | grep -vP &quot;(vendor|examples|_test)&quot; | xargs cat | grep -vP &quot;^\/\/&quot; | sed '/^\s*$/d' | wc -l
24485
</code></pre>
<pre><code class="language-bash">$ cd $GOPATH/src/github.com/scipipe/scipipe
$ find | grep &quot;\.go&quot; | grep -vP “(vendor|examples|_test)” | xargs cat | grep -vP &quot;^\/\/&quot; | sed '/^\s*$/d' | wc -l
1699
</code></pre>
<h3 id="workflow-implementation">Workflow Implementation</h3>
<p>Further, SciPipe was designed to primarily support highly dynamic workflow
constructs, where dynamic/on-line scheduling is needed. These workflows include
scenarios in which you are continuously chunking up and computing a dataset of
unknown size or parametrizing parts of the workflow with parameter values
extracted in an earlier part of the workflow. An example of the former would
be lazily processing data extracted from a database without saving the
temporary output to disk. An example of the latter would be doing a parameter
optimization to select, e.g., good gamma and cost values for libSVM before
actually training the model with the obtained parameters.</p>
<p>Also, where Pachyderm and Reflow provide manifest formats or DSLs for writing
workflows, SciPipe lets you write workflows directly in Go. SciPipe is
thus consumed as a programming-library rather than a framework.
This feature might scare off some users intimidated by Go’s relative
verboseness compared to specialized DSLs, but it also allows users to leverage
extremely powerful existing tooling and editor support for Go.</p>
<h3 id="deployment-integration">Deployment/Integration</h3>
<p>What is perhaps most interesting in the context of this comparison is the fact
that SciPipe workflows can be compiled to small static binaries. This
compilation makes it very easy to package up smaller SciPipe workflows in
individual containers and integrate them into tools like Pachyderm or Reflow or
other services. We thus imagine that SciPipe could be a complement to Pachyderm
or Reflow when highly dynamic workflow constructs are needed, which
may be a challenge to implement in the manifests and DSLs of Pachyderm or
Reflow.</p>
<h2 id="reflow-and-awe">Reflow and AWE?</h2>
<p>We know the least about AWE at this point, so we don’t want to venture too far
into a detailed comparison with Reflow (which is also new to us). However,
based on our reading of online materials, we can note that they seem to share
the focus on bioinformatics and cloud support. AWE additionally supports Common
Workflow Language and HPC clusters. We expect there to be some differences in
terms of storage, because AWE ships with its own storage solution and isn’t
based on a cloud offering like S3. Past that, we will leave it to the authors
of AWE and Reflow or the community to provide more comprehensive comparisons.</p>
<h2 id="in-summary">In Summary</h2>
<p>In summary, we think that it is extremely exciting and reassuring to see
continued innovation in the Go Data Science ecosystem, and we are excited to
see more and more data gophers and projects join the community. We also hope
this little overview will help users navigate the growing ecosystem of workflow
tools by highlighting some of their inherent differences.</p>
<hr />
<p><a href="https://twitter.com/smllmp" target="_blank">Samuel Lampa</a>, PhD Student at <a href="http://pharmb.io" target="_blank">Uppsala University</a><br></p>
<p><a href="https://www.linkedin.com/in/jon-ander-novella/" target="_blank">Jon Ander Novella</a>, Research Assistant at <a href="http://pharmb.io" target="_blank">Uppsala University</a><br></p>
<p><a href="https://twitter.com/dwhitena" target="_blank">Daniel Whitenack</a>, Data Scientist and Lead Developer Advocate at <a href="http://pachyderm.io" target="_blank">Pachyderm Inc.</a></p>
</description>
</item>
<item>
<title>Building an ML-Powered Game AI using TensorFlow in Go</title>
<link>http://gopherdata.io/post/build_ml_powered_game_ai_tensorflow/</link>
<pubDate>Thu, 10 Aug 2017 08:43:45 +1000</pubDate>
<guid>http://gopherdata.io/post/build_ml_powered_game_ai_tensorflow/</guid>
<description>
<p><em>Based on a lightning talk given at GopherCon 2017 &ldquo;Building an ML-Powered Game AI using TensorFlow in Go&rdquo; <a href="https://www.youtube.com/watch?v=oiorteQg9n0" target="_blank">Video</a> / <a href="https://github.com/gophercon/2017-talks/tree/master/lightningtalks/PeteGarcin-BuildingMLPoweredGameAIwithTensorFlow" target="_blank">Slides</a></em></p>
<p>(Author: Pete Garcin, Developer Advocate @ <a href="https://activestate.com" target="_blank">ActiveState</a>, @rawktron on <a href="https://twitter.com/rawktron" target="_blank">Twitter</a> and @peteg on Gophers Slack)</p>
<p>For GopherCon, we wanted to demonstrate some of the capabilities of the emerging machine learning and data science ecosystem in Go. Originally built as a demo for PyCon, I had put together a simple arcade space shooter game that features enemies powered by machine learning. It was a fun way to get folks engaged at conferences and to learn about the growing library of tools that are available. It also gave me an opportunity to build something non-trivial using machine learning techniques, and my background in games made this kind of interactive demo a good fit.</p>
<p>NeuroBlast is a vertically scrolling space shooter where you control a ship that tries to defeat increasing waves of enemies. Normally, these enemies fly in predefined formations, with predefined firing patterns, and come in waves. The big difference in NeuroBlast is that the enemies use machine learning to determine what their firing pattern should be.</p>
<h2 id="under-the-hood-tensorflow">Under the Hood: TensorFlow</h2>
<p>Go is one of the languages that has a TensorFlow client available, and so it was a great opportunity to port the original Python game to Go and demonstrate that it’s also possible to deploy trained models in Go applications.</p>
<p>It is worth noting that the Go TensorFlow client currently does not support training models, and so we relied on a model that was previously trained using the Python version of the game and then exported using the <code>SavedModelBuilder</code> functionality in order to load it in Go. This will export a TensorFlow graph as a protocol buffer and allow it to be loaded in Go using the <code>LoadSavedModel</code> function.</p>
<p>For the game portion, I used a library called <a href="https://github.com/faiface/pixel" target="_blank">Pixel</a> which is still early in development but has a really active community, and offered excellent stability and performance. I was pretty performance conscious when building and porting the game, so there are certain limitations such as non-pixel-perfect collisions, in order to ensure that the game could run acceptably under all conditions.</p>
<h3 id="training-the-neural-net">Training the Neural Net</h3>
<p>Our Neural Net is ultimately a very simple one &ndash; four inputs and a single output neuron. It will use supervised learning to do binary classification on a simple problem: was each shot a hit or a miss? It utilizes the delta between player and enemy position, and player and enemy velocity as the inputs. The single output neuron will fire if its activation value is &gt;= 0.5 and will not fire if it is &lt; 0.5.</p>
<p>When building the network, I initially had only a single hidden layer with 4 nodes but found that after training it, it was somewhat erratic. It seemed like it was very sensitive to the training data and would not ‘settle’ on a particular strategy in any consistent way. I experimented with a few different configurations, and ultimately settled on the one we used for the demo. It’s quite likely not the optimal setup, and may have more layers than is necessary. What appealed to me though was that even with a very small amount of training data, and regardless of how you trained it, it would consistently settle into a similar behaviour pattern which made it great for a floor demo where anyone could play or train the game.</p>
<p><img src="http://gopherdata.io/img/gameai/NNViz.png" alt="Neural Network Visualization" title="Neural Net Visualization" />
<em>The inputs are the four nodes at the top, with the output node (primed to fire here) at the bottom. Thicker lines represent higher weights. Activation values appear in the node centers.</em></p>
<p>The visualization was cobbled together by myself to run using the Pixel immediate mode drawing functions. Inspired by <a href="https://medium.com/deep-learning-101/how-to-generate-a-video-of-a-neural-network-learning-in-python-62f5c520e85c" target="_blank">this blog post</a>, the visualization here shows connections between nodes as either red or green lines. Green lines indicate positive weights that will bias the network towards “shooting”, and red values inhibit shooting. Thicker lines indicate higher weight values and thus “stronger” connections.</p>
<p>After training, the network consistently seems to converge on the following strategy:</p>
<p>If the player is within a reasonable cone of forward “vision” then fire indiscriminately.
If the player is not within that reasonable forward cone, then do not fire at all.</p>
<p>At first, it was very interesting to me that the network did not settle on the more obvious “just fire constantly” strategy, but given that it does receive training data that indicates that “misses” are undesirable, it makes sense that it would avoid firing shots with a low probability of hitting.</p>
<p><img src="http://gopherdata.io/img/gameai/GopherPos.png" alt="Positive Network Output" title="Positive Network Output" />
<em>In this image, notice that because I’m in a forward cone, most of the enemies are firing at my indiscriminately.</em></p>
<p><img src="http://gopherdata.io/img/gameai/GopherNeg.png" alt="Negative Network Output" title="Negative Network Output" />
<em>In this image, you’ll notice none of the enemies are firing because I am clearly outside their range of possible success. However, if you look at the activation values, you’ll see that the enemy who has just come onto the screen is about to blast me because his output is 0.83. Yikes!</em></p>
<p>In the training mode, the enemies fire randomly, and then each shot taken by the enemy is recorded as a hit or a miss along with its initial relative position/velocity values.</p>
<p>It’s worth noting that in early iterations of the game, I was passing in raw pixel values for positions and velocities. This meant that there was a really wide variation between the values in the input and I found that the network would just not really converge to a consistent behaviour. So, I normalized the input data to be roughly between 0.0-1.0 and found that it basically instantly converged to a usable behaviour. So, lesson for you kids: normalize your input data!</p>
<p>It&rsquo;s also important that you make all your input values framerate independent since the model is being trained in Python, any discrepancies between either the co-ordinate space or velocity values when running in Go will result in getting incorrect results back from the network.</p>
<p>Once the network is trained, when you play the game, every instance of an enemy spaceship uses its own instance of the neural network to make decisions about when it should fire.</p>
<h3 id="using-the-model-in-go">Using the model in Go</h3>
<p>As mentioned above, we exported our model from Python using the SavedModelBuilder and then need to load this in Go. The <code>SavedModelBuilder</code> will export the model into a folder, and you only need to specify that folder when loading in Go, along with the tag for your model. There are two available tags - TRAIN and SERVING. In our case, I used TRAIN but for deployment, it is suggested that you use the SERVING tag.</p>
<pre><code class="language-Go"> // bundle contains Session + Graph
bundle, err := tf.LoadSavedModel(&quot;exported_brain&quot;, []string{&quot;train&quot;}, nil)
</code></pre>
<p>This code will load your saved model and return a struct that contains pointers to the TensorFlow graph, and a new TensorFlow session that you can use for evaluation. However, at this stage many are left wondering - so, now what?</p>
<p>This is where current limitations in the Go binding and documentation show themselves. The key next step is to access the nodes in the TensorFlow graph for the input and output nodes. Right now, the only way to do this is to print the graph node names from Python when you are exporting. You do have the option of labelling nodes as you output them, but don&rsquo;t have the ability to access those nodes by their labels from Go.</p>
<p>In this case, once we have the names of the nodes, we need to access them in Go via the <code>Operation</code> method on the TensorFlow graph. Remember that because TensorFlow is storing our network as a series of computation operations, that&rsquo;s why it is called <code>Operation</code>.</p>
<pre><code class="language-Go"> inputop := bundle.Graph.Operation(&quot;dense_1_input&quot;)
outputop := bundle.Graph.Operation(&quot;dense_5/Sigmoid&quot;)
</code></pre>
<p>So now, we have both a Session with our Graph, and we have also found our input and output Operations, so now we&rsquo;re ready to send data to the graph and use it to evaluate that data. In our case, since we&rsquo;ve trained the graph using the relative position and velocity in the game, each frame, for each enemy we will take their relative position and velocity to the player and feed it into the network to get a decision back on whether or not we should fire at the player.</p>
<p>Our first step in having our enemies &ldquo;think&rdquo; is to build a new &lsquo;Tensor&rsquo; (which is like a vector) to feed into the network:</p>
<pre><code class="language-Go"> var column *tf.Tensor
column, err = tf.NewTensor([1][4]float32{{dx, dy, du, dv}})
results, err := bundle.Session.Run(map[tf.Output]*tf.Tensor{inputop.Output(0): column}, []tf.Output{outputop.Output(0)}, nil)
</code></pre>
<p>What you see above is that we&rsquo;re building a tensor from the relative position (dx, dy) and the relative velocity (du, dv) and then running our TensorFlow session, specifying which nodes are the input and output nodes. The session will then return the results in the form of an array. In our case, since we only have one output node, the array only has one entry.</p>
<p>Our enemies have almost made a decision - all we need to do is read the output value from their neuron. If value at the output node is greater than or equal to 0.5 we use that threshold to determine that we should fire at the player:</p>
<pre><code class="language-Go"> for _, result := range results {
if result.Value().([][]float32)[0][0] &gt;= 0.5 &amp;&amp; enemy.canfire {
// FIRE!!
}
}
</code></pre>
<p>And that&rsquo;s it! This is literally the only logic governing the attack behaviour of the enemies, and despite its simplicity, this network generates a compelling and interesting set of enemy behaviour. By applying this repeatedly to all enemies, we&rsquo;ve been able to create an enemy AI for our game.</p>
<p>There are obviously lots of easy and obvious ways to improve this, and to make it more sophisticated - but as a demonstration of utilizing a model loaded at runtime in Go and &lsquo;deployed&rsquo; into our game - it demonstrates the ease and power of these techniques and how accessible the tools are making them.</p>
<h2 id="next-steps">Next Steps</h2>
<p>The Machine Learning and Data Science ecosystem in Go is growing, and there are lots of exciting opportunties to contribute to a wide variety of projects - including TensorFlow.</p>
<p>In the near future, I plan to push this to GitHub as I had a number of requests at both GopherCon and PyCon to do so and would love to see others learn from this project as well as help to develop and expand its capabilities.</p>
<p><em>Update 08/29/17 - Full Source Code is now live on <a href="https://github.com/ActiveState/neuroblast" target="_blank">GitHub</a>!</em></p>
<p>If you want to ask questions about this project, feel free to hit me up on Twitter <a href="https://twitter.com/rawktron" target="_blank">@rawktron</a>.</p>
</description>
</item>
<item>
<title>Building a distributed Trump finder</title>
<link>http://gopherdata.io/post/distributed_trump_finder/</link>
<pubDate>Wed, 26 Apr 2017 12:00:00 +0000</pubDate>
<guid>http://gopherdata.io/post/distributed_trump_finder/</guid>
<description><p>(Author: Daniel Whitenack, @dwhitena on <a href="https://twitter.com/dwhitena" target="_blank">Twitter</a> and Gophers Slack)</p>
<p>If you haven&rsquo;t heard, there is a new kid on the machine learning block named <a href="https://machinebox.io/" target="_blank">Machine Box</a>. It&rsquo;s pretty cool, and you should check it out. Machine Box provides pre-built Docker images that enable easy, production ready, and reproducible machine learning operations. For example, you can get a &ldquo;facebox&rdquo; Docker image from Machine Box for facial recognition. When you run &ldquo;facebox,&rdquo; you get a full JSON API that lets you to easily &ldquo;teach&rdquo; facebox certain people&rsquo;s faces, identify those faces in images, and persist the &ldquo;state&rdquo; of trained facial recognition models.</p>
<p>Experimenting with &ldquo;facebox&rdquo; got me thinking about how it could be integrated into some of my workflows. In particular, I wanted to see how Machine Box images could be utilized as part of distributed data processing pipeline built with <a href="http://pachyderm.io/" target="_blank">Pachyderm</a>. Pachyderm builds, runs, and manages pipelines, such as machine learning workflows, based on Docker images. Thus, an integration of Machine Box images seems to be only natural.</p>
<p>And that&rsquo;s how the first (to my knowledge) distributed, Docker-ized, &ldquo;Trump-finding&rdquo; data pipeline came to be. In the sections below we will walk through the creation of a facial recognition pipeline that is able to find and tag the location of Donald Trump&rsquo;s face in images. Actually, this pipeline could be used to identify any faces, and we will illustrate this flexibility by updating the pipeline to learn a second face, Hillary Clinton.</p>
<p>The below sections assume that you have a Pachyderm cluster running, that <code>pachctl</code>(the Pachyderm CLI tool) is connected to that cluster, and that you have signed up for a Machine Box key (which you can do for free). All the code and more detailed instructions can be found <a href="https://github.com/dwhitena/pach-machine-box" target="_blank">here</a>.</p>
<p><strong>Create the pipeline inputs</strong>:</p>
<p>To train our facial recognition model, identify faces in images, and tag those faces with certain labels, we need to create three &ldquo;data repositories&rdquo; that will be the inputs to our Pachyderm pipeline:</p>
<ol>
<li><code>training</code> - which includes images of faces that we use to &ldquo;teach&rdquo; facebox</li>
<li><code>unidentified</code> - which includes images with faces we want to detect and identify</li>
<li><code>labels</code> - which includes label images that we will overlay on the unidentified images to indicate identified faces</li>
</ol>
<p>This can be done with <code>pachctl</code>:</p>
<pre><code class="language-sh">➔ pachctl create-repo training
➔ pachctl create-repo unidentified
➔ pachctl create-repo labels
➔ pachctl list-repo
NAME CREATED SIZE
labels 3 seconds ago 0 B
unidentified 11 seconds ago 0 B
training 17 seconds ago 0 B
➔ cd data/train/faces1/
➔ ls
trump1.jpg trump2.jpg trump3.jpg trump4.jpg trump5.jpg
➔ pachctl put-file training master -c -r -f .
➔ pachctl list-repo
NAME CREATED SIZE
training 5 minutes ago 486.2 KiB
labels 5 minutes ago 0 B
unidentified 5 minutes ago 0 B
➔ pachctl list-file training master
NAME TYPE SIZE
trump1.jpg file 78.98 KiB
trump2.jpg file 334.5 KiB
trump3.jpg file 11.63 KiB
trump4.jpg file 27.45 KiB
trump5.jpg file 33.6 KiB
➔ cd ../../labels/
➔ ls
clinton.jpg trump.jpg
➔ pachctl put-file labels master -c -r -f .
➔ cd ../unidentified/
➔ ls
image1.jpg image2.jpg
➔ pachctl put-file unidentified master -c -r -f .
➔ pachctl list-repo
NAME CREATED SIZE
unidentified 7 minutes ago 540.4 KiB
labels 7 minutes ago 15.44 KiB
training 7 minutes ago 486.2 KiB
</code></pre>
<p><strong>Train, or &ldquo;teach,&rdquo; facebox</strong>:</p>
<p>Next, we create a Pachyderm pipeline stage that will take the <code>training</code> data as input, provide those training images to facebox, and output the &ldquo;state&rdquo; of a trained model. This is done by providing Pachyderm with a pipeline spec, <a href="https://github.com/dwhitena/pach-machine-box/blob/master/pipelines/train.json" target="_blank">train.json</a>. This pipeline spec specifies the Docker image to use for data processing, the commands to execute in that Docker container, and what data is input to the pipeline.</p>
<p>In our particular case, <code>train.json</code> specifies that we should use an image based on the facebox image from Machine Box and execute a number of cURL commands to post the training data to facebox. Once those training images are provided to and processed by facebox, we specify another cURL command to export the state of the facebox model (for use later in our pipeline).</p>
<p>We are using a little bash magic here to perform these operations. However, it is very possible that, in the future, Machine Box will provide a more standardized command line implementation for these sorts of use cases.</p>
<pre><code class="language-sh">create-MB-pipeline.sh identify.json tag.json train.json
➔ ./create-MB-pipeline.sh train.json
➔ pachctl list-pipeline
NAME INPUT OUTPUT STATE
model training model/master running
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
3425a7a0-543e-4e2a-a244-a3982c527248 model/- 9 seconds ago - 1 0 / 1 running
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 5 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-repo
NAME CREATED SIZE
model 5 minutes ago 4.118 KiB
unidentified 18 minutes ago 540.4 KiB
labels 18 minutes ago 15.44 KiB
training 19 minutes ago 486.2 KiB
➔ pachctl list-file model master
NAME TYPE SIZE
state.facebox file 4.118 KiB
</code></pre>
<p>As you can see the output of this pipeline is a <code>.facebox</code> file that contained the trained state of our facebox model.</p>
<p><strong>Use the trained facebox to identify faces</strong>:</p>
<p>We then launch another Pachyderm pipeline, based on an <a href="https://github.com/dwhitena/pach-machine-box/blob/master/pipelines/identify.json" target="_blank">identify.json</a> pipeline specification, to identify faces within the <code>unidentified</code> images. This pipeline will take the persisted state of our model in <code>model</code> along with the <code>unidentified</code> images as input. It will also execute cURL commands to interact with facebox, and it will output indications of identified faces to JSON files, one per <code>unidentified</code> image.</p>
<pre><code class="language-sh">➔ ./create-MB-pipeline.sh identify.json
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
281d4393-05c8-44bf-b5de-231cea0fc022 identify/- 6 seconds ago - 0 0 / 2 running
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 8 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
281d4393-05c8-44bf-b5de-231cea0fc022 identify/287fc78a4cdf42d89142d46fb5f689d9 About a minute ago 53 seconds 0 2 / 2 success
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 9 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-repo
NAME CREATED SIZE
identify About a minute ago 1.932 KiB
model 10 minutes ago 4.118 KiB
unidentified 23 minutes ago 540.4 KiB
labels 23 minutes ago 15.44 KiB
training 24 minutes ago 486.2 KiB
➔ pachctl list-file identify master
NAME TYPE SIZE
image1.json file 1.593 KiB
image2.json file 347 B
</code></pre>
<p>If we look at the JSON output for, e.g., <code>image1.jpg</code>, we can see that there is a portion of the file that clearly identifies Donald Trump in the image along with the location and size of his face in the image:</p>
<pre><code>{
&quot;success&quot;: true,
&quot;facesCount&quot;: 13,
&quot;faces&quot;: [
...
...
{
&quot;rect&quot;: {
&quot;top&quot;: 175,
&quot;left&quot;: 975,
&quot;width&quot;: 108,
&quot;height&quot;: 108
},
&quot;id&quot;: &quot;58ff31510f7707a01fb3e2f4d39f26dc&quot;,
&quot;name&quot;: &quot;trump&quot;,
&quot;matched&quot;: true
},
...
...
]
}
</code></pre>
<p><strong>Tagging identified faces in the images</strong>:</p>
<p>We are most of the way there! We have identified Trump in the <code>unidentified</code> images, but the JSON output isn&rsquo;t the most visually appealling. As such, let&rsquo;s overlay a label on the images at the location of Trump&rsquo;s face.</p>
<p>To do this, we can use a <a href="https://github.com/dwhitena/pach-machine-box/blob/master/tagimage/main.go" target="_blank">simple Go program</a> to draw the label image on the <code>unidentified</code> image at the appropriate location. This part of the pipeline is specified by a <a href="https://github.com/dwhitena/pach-machine-box/blob/master/pipelines/tag.json" target="_blank">tag.json</a> pipeline specification, and can be created as follows:</p>
<pre><code class="language-sh">➔ pachctl create-pipeline -f tag.json
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
cd284a28-6c97-4236-9f6d-717346c60f24 tag/- 2 seconds ago - 0 0 / 2 running
281d4393-05c8-44bf-b5de-231cea0fc022 identify/287fc78a4cdf42d89142d46fb5f689d9 5 minutes ago 53 seconds 0 2 / 2 success
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 13 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
cd284a28-6c97-4236-9f6d-717346c60f24 tag/ae747e8032704b6cae6ae7bba064c3c3 25 seconds ago 11 seconds 0 2 / 2 success
281d4393-05c8-44bf-b5de-231cea0fc022 identify/287fc78a4cdf42d89142d46fb5f689d9 5 minutes ago 53 seconds 0 2 / 2 success
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 14 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-repo
NAME CREATED SIZE
tag 30 seconds ago 591.3 KiB
identify 5 minutes ago 1.932 KiB
model 14 minutes ago 4.118 KiB
unidentified 27 minutes ago 540.4 KiB
labels 27 minutes ago 15.44 KiB
training 27 minutes ago 486.2 KiB
➔ pachctl list-file tag master
NAME TYPE SIZE
tagged_image1.jpg file 557 KiB
tagged_image2.jpg file 34.35 KiB
</code></pre>
<p>As you can see, we now have two &ldquo;tagged&rdquo; versions of the images in the output <code>tag</code> data repository. If we get these images, we can see that&hellip; Boom! Our Trump finder works:</p>
<p><img src="https://raw.githubusercontent.com/dwhitena/pach-machine-box/master/tagged_images1.jpg" alt="alt text" /></p>
<p><strong>Teaching a new face, updating the output</strong>:</p>
<p>Our pipeline isn&rsquo;t restricted to Trump or any one face. Actually, we can teach facebox another face by updating our <code>training</code>. Moreover, because Pachyderm versions your data and knows what data is new, it can automatically update all our results once facebox learns the new face:</p>
<pre><code class="language-sh">➔ cd ../data/train/faces2/
➔ ls
clinton1.jpg clinton2.jpg clinton3.jpg clinton4.jpg
➔ pachctl put-file training master -c -r -f .
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
56e24ac0-0430-4fa4-aa8b-08de5c1884db model/- 4 seconds ago - 0 0 / 1 running
cd284a28-6c97-4236-9f6d-717346c60f24 tag/ae747e8032704b6cae6ae7bba064c3c3 6 minutes ago 11 seconds 0 2 / 2 success
281d4393-05c8-44bf-b5de-231cea0fc022 identify/287fc78a4cdf42d89142d46fb5f689d9 11 minutes ago 53 seconds 0 2 / 2 success
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 20 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-job
ID OUTPUT COMMIT STARTED DURATION RESTART PROGRESS STATE
6aa6c995-58ce-445d-999a-eb0e0690b041 tag/7cbd2584d4f0472abbca0d9e015b9829 5 seconds ago 1 seconds 0 2 / 2 success
8a7961b7-1085-404a-b0ee-66034fae7212 identify/1bc94ec558e44e0cb45ed5ab7d9f9674 59 seconds ago 54 seconds 0 2 / 2 success
56e24ac0-0430-4fa4-aa8b-08de5c1884db model/002f16b63a4345a4bc6bdf5510c9faac About a minute ago 19 seconds 0 1 / 1 success
cd284a28-6c97-4236-9f6d-717346c60f24 tag/ae747e8032704b6cae6ae7bba064c3c3 8 minutes ago 11 seconds 0 2 / 2 success
281d4393-05c8-44bf-b5de-231cea0fc022 identify/287fc78a4cdf42d89142d46fb5f689d9 13 minutes ago 53 seconds 0 2 / 2 success
3425a7a0-543e-4e2a-a244-a3982c527248 model/1b9c158e33394056a18041a4a86cb54a 21 minutes ago 5 minutes 1 1 / 1 success
➔ pachctl list-file tag master
NAME TYPE SIZE
tagged_image1.jpg file 557 KiB
tagged_image2.jpg file 36.03 KiB
</code></pre>
<p>Now if we look at our images, we find that everything has been updated without any annoying manual work on our hands:</p>
<p><img src="https://raw.githubusercontent.com/dwhitena/pach-machine-box/master/tagged_images2.jpg" alt="alt text" /></p>
<p><strong>Conclusion/Resources</strong>:</p>
<p>As you can see, Machine Box and Pachyderm make it really quick and easy to deploy a distributed, machine learning data pipeline. Be sure to:</p>
<ul>
<li>Visit <a href="https://github.com/dwhitena/pach-machine-box" target="_blank">this repo</a> to get the code and pipeline specs, so you can create your own Trump finder!</li>
<li>Join the <a href="http://slack.pachyderm.io/" target="_blank">Pachyderm Slack team</a> to get help implementing your ML pipelines, and participate in the discussion in the #data-science channel on Gophers Slack.</li>
<li>Follow <a href="https://twitter.com/pachydermIO" target="_blank">Pachyderm on Twitter</a>,</li>
<li>Sign up for a free <a href="https://machinebox.io/" target="_blank">Machine Box</a> API key, and</li>
<li>Follow <a href="https://twitter.com/machineboxio" target="_blank">Machine Box on Twitter</a>.</li>
</ul>
</description>
</item>
<item>
<title>Deep Learning from Scratch in Go - Part 1: Equations Are Graphs</title>
<link>http://gopherdata.io/post/deeplearning_in_go_part_1/</link>
<pubDate>Wed, 19 Apr 2017 08:43:45 +1000</pubDate>
<guid>http://gopherdata.io/post/deeplearning_in_go_part_1/</guid>
<description>
<p>(Author: Chewxy, @chewxy on <a href="https://twitter.com/chewxy" target="_blank">Twitter</a> and Gophers Slack)</p>
<p>Welcome to the first part of many about writing deep learning algorithms in Go. The goal of this series is to go from having no knowledge at all to implementing some of the latest developments in this area.</p>
<p><a href="https://en.wikipedia.org/wiki/Deep_learning" target="_blank">Deep learning</a> is not new. In fact the idea of deep learning was spawned in the early 1980s. What&rsquo;s changed since then is our computers - they have gotten much much more powerful. In this blog post we&rsquo;ll start with something familiar, and edge towards building a conceptual model of deep learning. We won&rsquo;t define deep learning for the first few posts, so don&rsquo;t worry so much about the term.</p>
<p>There are a few terms of clarification to be made before we begin proper. In this series, the word &ldquo;graph&rdquo; refers to the concept of graph as used in <a href="https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)" target="_blank">graph theory</a>. For the other kind of &ldquo;graph&rdquo; which is usually used for data visualization, I&rsquo;ll use the term &ldquo;chart&rdquo;.</p>
<h2 id="computation">Computation</h2>
<p>I&rsquo;m going to start by making a claim: all programs can be represented as graphs. This claim is not new, of course. Nor is it bold or revolutionary. It&rsquo;s the fundamental theory that computer scientists have been working on ever since the birth of the field of computation. But you may have missed it. If you have missed it, the logic goes as such:</p>
<ol>
<li>All modern computer programs run on what essentially is a <a href="https://en.wikipedia.org/wiki/Turing_machine" target="_blank">Turing Machine</a>.</li>
<li>All Turing machines are equivalent to untyped lambda calculus (this is commonly known as the <a href="https://en.wikipedia.org/wiki/Church_Turing_thesis" target="_blank">Church-Turing thesis</a>)</li>
<li>Lambda calculus can be represented as graphs.</li>
<li>Therefore all programs can be represented as graphs.</li>
</ol>
<p>To make this idea more concrete, let&rsquo;s look at a simple program:</p>
<pre><code class="language-go">func main() {
fmt.Printf(&quot;%v&quot;, 1+1)
}
</code></pre>
<p>This generates an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" target="_blank">abstract syntax tree</a> like so (the AST was generated with a library built on top of <a href="https://github.com/yuroyoro/goast-viewer" target="_blank">goast-viewer</a>):</p>
<div style="margin-left:auto; margin-right:auto;">
<svg style="width:100%; height:auto;"
viewBox="0.00 0.00 1167.98 548.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 544)">
<title>%3</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-544 1163.98,-544 1163.98,4 -4,4"/>
<!-- mainFn -->
<g id="node1" class="node"><title>mainFn</title>
<ellipse fill="none" stroke="black" cx="445.942" cy="-522" rx="52.7911" ry="18"/>
<text text-anchor="middle" x="445.942" y="-518.3" font-family="Times,serif" font-size="14.00">func main()</text>
</g>
<!-- mainBody -->
<g id="node2" class="node"><title>mainBody</title>
<ellipse fill="none" stroke="black" cx="445.942" cy="-450" rx="61.1893" ry="18"/>
<text text-anchor="middle" x="445.942" y="-446.3" font-family="Times,serif" font-size="14.00">*ast.BlocStmt</text>
</g>
<!-- mainFn&#45;&gt;mainBody -->
<g id="edge1" class="edge"><title>mainFn&#45;&gt;mainBody</title>
<path fill="none" stroke="black" d="M445.942,-503.697C445.942,-495.983 445.942,-486.712 445.942,-478.112"/>
<polygon fill="black" stroke="black" points="449.442,-478.104 445.942,-468.104 442.442,-478.104 449.442,-478.104"/>
</g>
<!-- bodyList -->
<g id="node3" class="node"><title>bodyList</title>
<ellipse fill="none" stroke="black" cx="445.942" cy="-378" rx="46.2923" ry="18"/>
<text text-anchor="middle" x="445.942" y="-374.3" font-family="Times,serif" font-size="14.00">[]ast.Stmt</text>
</g>
<!-- mainBody&#45;&gt;bodyList -->
<g id="edge2" class="edge"><title>mainBody&#45;&gt;bodyList</title>
<path fill="none" stroke="black" d="M445.942,-431.697C445.942,-423.983 445.942,-414.712 445.942,-406.112"/>
<polygon fill="black" stroke="black" points="449.442,-406.104 445.942,-396.104 442.442,-406.104 449.442,-406.104"/>
</g>
<!-- list0 -->
<g id="node4" class="node"><title>list0</title>
<ellipse fill="none" stroke="black" cx="445.942" cy="-306" rx="61.99" ry="18"/>
<text text-anchor="middle" x="445.942" y="-302.3" font-family="Times,serif" font-size="14.00">*ast.ExprStmt</text>
</g>
<!-- bodyList&#45;&gt;list0 -->
<g id="edge3" class="edge"><title>bodyList&#45;&gt;list0</title>
<path fill="none" stroke="black" d="M445.942,-359.697C445.942,-351.983 445.942,-342.712 445.942,-334.112"/>
<polygon fill="black" stroke="black" points="449.442,-334.104 445.942,-324.104 442.442,-334.104 449.442,-334.104"/>
</g>
<!-- call -->
<g id="node5" class="node"><title>call</title>
<ellipse fill="none" stroke="black" cx="445.942" cy="-234" rx="59.2899" ry="18"/>
<text text-anchor="middle" x="445.942" y="-230.3" font-family="Times,serif" font-size="14.00">*ast.CallExpr</text>
</g>
<!-- list0&#45;&gt;call -->
<g id="edge4" class="edge"><title>list0&#45;&gt;call</title>
<path fill="none" stroke="black" d="M445.942,-287.697C445.942,-279.983 445.942,-270.712 445.942,-262.112"/>
<polygon fill="black" stroke="black" points="449.442,-262.104 445.942,-252.104 442.442,-262.104 449.442,-262.104"/>
</g>
<!-- fn -->
<g id="node6" class="node"><title>fn</title>
<ellipse fill="none" stroke="black" cx="303.942" cy="-162" rx="73.387" ry="18"/>
<text text-anchor="middle" x="303.942" y="-158.3" font-family="Times,serif" font-size="14.00">*ast.SelectorExpr</text>
</g>
<!-- call&#45;&gt;fn -->
<g id="edge5" class="edge"><title>call&#45;&gt;fn</title>
<path fill="none" stroke="black" d="M416.174,-218.326C395.302,-208.037 367.156,-194.162 344.272,-182.881"/>
<polygon fill="black" stroke="black" points="345.662,-179.664 335.145,-178.382 342.567,-185.943 345.662,-179.664"/>
</g>
<!-- args -->
<g id="node9" class="node"><title>args</title>
<ellipse fill="none" stroke="black" cx="587.942" cy="-162" rx="46.2923" ry="18"/>
<text text-anchor="middle" x="587.942" y="-158.3" font-family="Times,serif" font-size="14.00">[]ast.Expr</text>
</g>
<!-- call&#45;&gt;args -->
<g id="edge8" class="edge"><title>call&#45;&gt;args</title>
<path fill="none" stroke="black" d="M475.71,-218.326C497.582,-207.544 527.443,-192.823 550.866,-181.277"/>
<polygon fill="black" stroke="black" points="552.721,-184.265 560.142,-176.704 549.625,-177.986 552.721,-184.265"/>
</g>
<!-- fnPkg -->
<g id="node7" class="node"><title>fnPkg</title>
<ellipse fill="none" stroke="black" cx="92.9418" cy="-90" rx="92.8835" ry="18"/>
<text text-anchor="middle" x="92.9418" y="-86.3" font-family="Times,serif" font-size="14.00">*ast.Ident (Name: fmt)</text>
</g>
<!-- fn&#45;&gt;fnPkg -->
<g id="edge6" class="edge"><title>fn&#45;&gt;fnPkg</title>
<path fill="none" stroke="black" d="M262.255,-147.17C229.431,-136.281 183.405,-121.012 147.625,-109.141"/>
<polygon fill="black" stroke="black" points="148.418,-105.717 137.825,-105.89 146.214,-112.361 148.418,-105.717"/>
</g>
<!-- fnFn -->
<g id="node8" class="node"><title>fnFn</title>
<ellipse fill="none" stroke="black" cx="303.942" cy="-90" rx="100.182" ry="18"/>
<text text-anchor="middle" x="303.942" y="-86.3" font-family="Times,serif" font-size="14.00">*ast.Ident (Name: Printf)</text>
</g>
<!-- fn&#45;&gt;fnFn -->
<g id="edge7" class="edge"><title>fn&#45;&gt;fnFn</title>
<path fill="none" stroke="black" d="M303.942,-143.697C303.942,-135.983 303.942,-126.712 303.942,-118.112"/>
<polygon fill="black" stroke="black" points="307.442,-118.104 303.942,-108.104 300.442,-118.104 307.442,-118.104"/>
</g>
<!-- args0 -->
<g id="node10" class="node"><title>args0</title>
<ellipse fill="none" stroke="black" cx="587.942" cy="-90" rx="165.971" ry="18"/>
<text text-anchor="middle" x="587.942" y="-86.3" font-family="Times,serif" font-size="14.00">*ast.BasicLit (Kind: STRING) (Value: %v)</text>
</g>
<!-- args&#45;&gt;args0 -->
<g id="edge9" class="edge"><title>args&#45;&gt;args0</title>
<path fill="none" stroke="black" d="M587.942,-143.697C587.942,-135.983 587.942,-126.712 587.942,-118.112"/>
<polygon fill="black" stroke="black" points="591.442,-118.104 587.942,-108.104 584.442,-118.104 591.442,-118.104"/>
</g>
<!-- args1 -->
<g id="node11" class="node"><title>args1</title>
<ellipse fill="none" stroke="black" cx="868.942" cy="-90" rx="97.4827" ry="18"/>
<text text-anchor="middle" x="868.942" y="-86.3" font-family="Times,serif" font-size="14.00">*ast.BinaryExpr (Op: +)</text>
</g>
<!-- args&#45;&gt;args1 -->
<g id="edge10" class="edge"><title>args&#45;&gt;args1</title>
<path fill="none" stroke="black" d="M625.839,-151.559C671.25,-140.247 748.104,-121.102 803.251,-107.364"/>
<polygon fill="black" stroke="black" points="804.426,-110.679 813.284,-104.865 802.734,-103.886 804.426,-110.679"/>
</g>
<!-- lhs -->
<g id="node12" class="node"><title>lhs</title>
<ellipse fill="none" stroke="black" cx="718.942" cy="-18" rx="141.075" ry="18"/>
<text text-anchor="middle" x="718.942" y="-14.3" font-family="Times,serif" font-size="14.00">*ast.BasicLit (Kind: INT) (Value: 1)</text>
</g>
<!-- args1&#45;&gt;lhs -->
<g id="edge11" class="edge"><title>args1&#45;&gt;lhs</title>
<path fill="none" stroke="black" d="M834.904,-73.1159C813.785,-63.26 786.413,-50.4867 763.567,-39.825"/>
<polygon fill="black" stroke="black" points="764.951,-36.6089 754.409,-35.5516 761.991,-42.9522 764.951,-36.6089"/>
</g>
<!-- rhs -->
<g id="node13" class="node"><title>rhs</title>
<ellipse fill="none" stroke="black" cx="1018.94" cy="-18" rx="141.075" ry="18"/>
<text text-anchor="middle" x="1018.94" y="-14.3" font-family="Times,serif" font-size="14.00">*ast.BasicLit (Kind: INT) (Value: 1)</text>
</g>
<!-- args1&#45;&gt;rhs -->
<g id="edge12" class="edge"><title>args1&#45;&gt;rhs</title>
<path fill="none" stroke="black" d="M902.979,-73.1159C924.099,-63.26 951.47,-50.4867 974.317,-39.825"/>
<polygon fill="black" stroke="black" points="975.892,-42.9522 983.474,-35.5516 972.932,-36.6089 975.892,-42.9522"/>
</g>
</g>
</svg>
</div>
<p>By this, we can also say that any equation that can be represented as a computer program, and a computer program can be represented as a graph. In particular, let&rsquo;s zoom in <code>1+1</code> part:</p>
<p>This corresponds to this part of the cleaned up graph (with unnecessary nodes removed):</p>
<div style="margin-left:auto; margin-right:auto;">
<svg style="width:100%; height:auto;"
viewBox="0.00 0.00 590.07 116.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 112)">
<title>%3</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-112 586.075,-112 586.075,4 -4,4"/>
<!-- args1 -->
<g id="node1" class="node"><title>args1</title>
<ellipse fill="none" stroke="black" cx="291.037" cy="-90" rx="97.4827" ry="18"/>
<text text-anchor="middle" x="291.037" y="-86.3" font-family="Times,serif" font-size="14.00">*ast.BinaryExpr (Op: +)</text>
</g>
<!-- lhs -->
<g id="node2" class="node"><title>lhs</title>
<ellipse fill="none" stroke="black" cx="141.037" cy="-18" rx="141.075" ry="18"/>
<text text-anchor="middle" x="141.037" y="-14.3" font-family="Times,serif" font-size="14.00">*ast.BasicLit (Kind: INT) (Value: 1)</text>
</g>
<!-- args1&#45;&gt;lhs -->
<g id="edge1" class="edge"><title>args1&#45;&gt;lhs</title>
<path fill="none" stroke="black" d="M257,-73.1159C235.88,-63.26 208.509,-50.4867 185.663,-39.825"/>
<polygon fill="black" stroke="black" points="187.047,-36.6089 176.505,-35.5516 184.087,-42.9522 187.047,-36.6089"/>
</g>
<!-- rhs -->
<g id="node3" class="node"><title>rhs</title>
<ellipse fill="none" stroke="black" cx="441.037" cy="-18" rx="141.075" ry="18"/>
<text text-anchor="middle" x="441.037" y="-14.3" font-family="Times,serif" font-size="14.00">*ast.BasicLit (Kind: INT) (Value: 1)</text>
</g>
<!-- args1&#45;&gt;rhs -->
<g id="edge2" class="edge"><title>args1&#45;&gt;rhs</title>
<path fill="none" stroke="black" d="M325.075,-73.1159C346.195,-63.26 373.566,-50.4867 396.412,-39.825"/>
<polygon fill="black" stroke="black" points="397.988,-42.9522 405.57,-35.5516 395.028,-36.6089 397.988,-42.9522"/>
</g>
</g>
</svg>
</div>
<p>The graph is traversed in a depth-first manner, starting from the top. The values of the program flow from bottom to top. When the program runs, it starts right at the top. The node will not be resolved until the dependent nodes have been evaluated. The arrows point to what each node depends on. So for example, the value of the <code>*ast.BinaryExpr</code> node is dependent on the values of <code>*ast.BasicLit (Kind: INT)</code>. Since we know both values are <code>1</code>, and we know what <code>+</code> does, we know that the value at the node <code>*ast.BinaryExpr</code> is <code>2</code>.</p>
<h2 id="equations-as-graphs">Equations As Graphs</h2>
<p>Now why did we spend all that time show 1+1 in graph form? Well, it&rsquo;s because deep learning is really in its core, just a bunch of mathematical equations. Wait, don&rsquo;t go yet! It&rsquo;s not that scary. I am personally of the opinion that one can&rsquo;t really do deep learning (or any machine learning, really) without understanding the mathematics behind it. And in my experience there hasn&rsquo;t been a better way to learn it than visually, if only to internalize the concepts.</p>
<p>Most deep learning libraries like <a href="https://tensorflow.org" target="_blank">Tensorflow</a>, <a href="https://deeplearning.org/theano" target="_blank">Theano</a>, or even my own for Go - <a href="https://github.com/chewxy/gorgonia" target="_blank">Gorgonia</a>, rely on this core concept that equations are representable by graphs. More importantly, these libraries expose the equation graphs as objects that can be manipulated by the programmer.</p>
<p>So instead of the program above, we&rsquo;d create something like this:</p>
<pre><code class="language-go">func main() {
// Create a graph.
g := G.NewGraph()
// Create a node called &quot;x&quot; with the value 1.
x := G.NodeFromAny(g, 1, G.WithName(&quot;x&quot;))
// Create a node called &quot;y&quot; with the value 1.
y := G.NodeFromAny(g, 1, G.WithName(&quot;y&quot;))
// z := x + y
z := G.Must(G.Add(x, y))
// Create a VM to execute the graph.
vm := G.NewTapeMachine(g)
// Run the VM. Errors are not checked.
vm.RunAll()
// Print the value of z.
fmt.Printf(&quot;%v&quot;, z.Value())
}
</code></pre>
<p>The equation graph looks like this:</p>
<div style="margin-left:auto; margin-right:auto;">
<svg style="width:100%; height:auto;"
viewBox="0.00 0.00 715.00 360.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 356)">
<title>fullGraph</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-356 1109.3,-356 1109.3,4 -4,4"/>
<g id="clust1" class="cluster"><title>cluster_expressionGraph</title>
<polygon fill="none" stroke="black" points="8,-192 8,-344 574,-344 574,-192 8,-192"/>
<text text-anchor="middle" x="291" y="-328.8" font-family="Times,serif" font-size="14.00">expressionGraph</text>
</g>
<!-- Node_0xc420146300 -->
<g id="node1" class="node"><title>Node_0xc420146300</title>
<polygon fill="none" stroke="#ff0000" points="206,-287.5 206,-308.5 254,-308.5 254,-287.5 206,-287.5"/>
<text text-anchor="start" x="225.5" y="-294.3" font-family="monospace" font-size="14.00">2</text>
<polygon fill="none" stroke="#ff0000" points="254,-287.5 254,-308.5 558,-308.5 558,-287.5 254,-287.5"/>
<text text-anchor="start" x="257" y="-294.3" font-family="monospace" font-size="14.00">+ false(%a8d96c31, %82d6f1c8) :: int</text>
<polygon fill="none" stroke="#ff0000" points="206,-266.5 206,-287.5 254,-287.5 254,-266.5 206,-266.5"/>
<text text-anchor="start" x="221.5" y="-273.3" font-family="monospace" font-size="14.00">Op</text>
<polygon fill="none" stroke="#ff0000" points="254,-266.5 254,-287.5 558,-287.5 558,-266.5 254,-266.5"/>
<text text-anchor="start" x="323" y="-273.3" font-family="monospace" font-size="14.00">+ false :: a → a → a</text>
<polygon fill="none" stroke="#ff0000" points="206,-245.5 206,-266.5 558,-266.5 558,-245.5 206,-245.5"/>
<text text-anchor="start" x="373.5" y="-252.3" font-family="monospace" font-size="14.00">()</text>
<polygon fill="none" stroke="#ff0000" points="206,-224.5 206,-245.5 558,-245.5 558,-224.5 206,-224.5"/>
<text text-anchor="start" x="373.5" y="-231.3" font-family="monospace" font-size="14.00">&#45;1</text>
<polygon fill="none" stroke="#ff0000" points="206,-203.5 206,-224.5 254,-224.5 254,-203.5 206,-203.5"/>
<text text-anchor="start" x="209" y="-210.3" font-family="monospace" font-size="14.00">Value</text>
<polygon fill="none" stroke="#ff0000" points="254,-203.5 254,-224.5 558,-224.5 558,-203.5 254,-203.5"/>
<text text-anchor="start" x="377" y="-210.3" font-family="monospace" font-size="14.00">2 :: int</text>
</g>
<!-- Node_0xc420146000 -->
<g id="node2" class="node"><title>Node_0xc420146000</title>
<polygon fill="lightyellow" stroke="none" points="217.5,-4 217.5,-88 338.5,-88 338.5,-4 217.5,-4"/>
<polygon fill="none" stroke="#00ff00" points="218,-67 218,-88 266,-88 266,-67 218,-67"/>
<text text-anchor="start" x="237.5" y="-73.8" font-family="monospace" font-size="14.00">0</text>
<polygon fill="none" stroke="#00ff00" points="266,-67 266,-88 339,-88 339,-67 266,-67"/>
<text text-anchor="start" x="269" y="-73.8" font-family="monospace" font-size="14.00">x :: int</text>
<polygon fill="none" stroke="#00ff00" points="218,-46 218,-67 339,-67 339,-46 218,-46"/>
<text text-anchor="start" x="270" y="-52.8" font-family="monospace" font-size="14.00">()</text>
<polygon fill="none" stroke="#00ff00" points="218,-25 218,-46 339,-46 339,-25 218,-25"/>
<text text-anchor="start" x="270" y="-31.8" font-family="monospace" font-size="14.00">&#45;1</text>
<polygon fill="none" stroke="#00ff00" points="218,-4 218,-25 266,-25 266,-4 218,-4"/>
<text text-anchor="start" x="221" y="-10.8" font-family="monospace" font-size="14.00">Value</text>
<polygon fill="none" stroke="#00ff00" points="266,-4 266,-25 339,-25 339,-4 266,-4"/>
<text text-anchor="start" x="273.5" y="-10.8" font-family="monospace" font-size="14.00">1 :: int</text>
</g>
<!-- Node_0xc420146300&#45;&gt;Node_0xc420146000 -->
<g id="edge1" class="edge"><title>Node_0xc420146300⚓️s&#45;&gt;Node_0xc420146000⚓️n</title>
<path fill="none" stroke="black" d="M382,-202.5C382,-137.689 288.68,-155.213 278.837,-99.0855"/>
<polygon fill="black" stroke="black" points="282.315,-98.6762 278,-89 275.339,-99.2553 282.315,-98.6762"/>
<text text-anchor="middle" x="385.75" y="-191.3" font-family="Times,serif" font-size="14.00"> 0 </text>
</g>
<!-- Node_0xc420146240 -->
<g id="node3" class="node"><title>Node_0xc420146240</title>
<polygon fill="lightyellow" stroke="none" points="426.5,-4 426.5,-88 547.5,-88 547.5,-4 426.5,-4"/>
<polygon fill="none" stroke="#00ff00" points="427,-67 427,-88 475,-88 475,-67 427,-67"/>
<text text-anchor="start" x="446.5" y="-73.8" font-family="monospace" font-size="14.00">1</text>
<polygon fill="none" stroke="#00ff00" points="475,-67 475,-88 548,-88 548,-67 475,-67"/>
<text text-anchor="start" x="478" y="-73.8" font-family="monospace" font-size="14.00">y :: int</text>
<polygon fill="none" stroke="#00ff00" points="427,-46 427,-67 548,-67 548,-46 427,-46"/>
<text text-anchor="start" x="479" y="-52.8" font-family="monospace" font-size="14.00">()</text>
<polygon fill="none" stroke="#00ff00" points="427,-25 427,-46 548,-46 548,-25 427,-25"/>
<text text-anchor="start" x="479" y="-31.8" font-family="monospace" font-size="14.00">&#45;1</text>
<polygon fill="none" stroke="#00ff00" points="427,-4 427,-25 475,-25 475,-4 427,-4"/>
<text text-anchor="start" x="430" y="-10.8" font-family="monospace" font-size="14.00">Value</text>
<polygon fill="none" stroke="#00ff00" points="475,-4 475,-25 548,-25 548,-4 475,-4"/>
<text text-anchor="start" x="482.5" y="-10.8" font-family="monospace" font-size="14.00">1 :: int</text>
</g>
<!-- Node_0xc420146300&#45;&gt;Node_0xc420146240 -->
<g id="edge2" class="edge"><title>Node_0xc420146300⚓️s&#45;&gt;Node_0xc420146240⚓️n</title>
<path fill="none" stroke="black" d="M382,-202.5C382,-137.404 476.218,-155.453 486.155,-99.1258"/>
<polygon fill="black" stroke="black" points="489.656,-99.2565 487,-89 482.68,-98.6742 489.656,-99.2565"/>
<text text-anchor="middle" x="374.5" y="-191.3" font-family="Times,serif" font-size="14.00"> 1 </text>
</g>
<!-- outsideRoot -->
<!-- insideInputs -->
<!-- outsideRoot&#45;&gt;insideInputs -->
<!-- outsideExprG -->
<!-- outsideRoot&#45;&gt;outsideExprG -->
<!-- insideExprG -->
<!-- insideInputs&#45;&gt;insideExprG -->
<!-- outsideExprG&#45;&gt;insideExprG -->
</g>
</svg>
</div>
<h2 id="why-graph-objects">Why Graph Objects?</h2>
<p>So far you might be thinking - if all computer programs are graphs, and all mathematical equations are graphs, we could just program the mathematical equations in, right? Why write the above example with object graphs when a simple <code>fmt.Printf(&quot;%v&quot;, 1+1)</code> would do? Afterall, the graphs are mostly the same. Having a graph object at this point seem like a silly amount of overhead.</p>
<p>You would be right. For simple equations, having a programmer manipulatable graph object is really overkill (unless you live in Java land, where it&rsquo;s classes all the way down).</p>
<p>I would however, posit at least three advantages of having a graph object. All of these have got to do with reducing human errors.</p>
<h3 id="numerical-stability">Numerical Stability</h3>
<p>Consider the equation $y = log(1 + x)$. This equation is not <a href="https://en.wikipedia.org/wiki/Numerical_stability" target="_blank">numerically stable</a> - for very small values of <code>x</code>, the answer will most likely be wrong. This is because of the way <code>float64</code> is designed - a <code>float64</code> does not have enough bits to be able to tell apart <code>1</code> and <code>1 + 10e-16</code>. In fact, the correct way to do $ y = log(1 + x)$ is to use the built in library function <code>math.Log1p</code>. It can be shown in this simple program:</p>
<pre><code class="language-go">func main() {
fmt.Printf(&quot;%v\n&quot;, math.Log(1.0+10e-16))
fmt.Printf(&quot;%v\n&quot;, math.Log1p(10e-16))
}
1.110223024625156e-15 // wrong
9.999999999999995e-16 // correct
</code></pre>
<p>Now, of course the programmer may be well aware of this issue and opt to use <code>math.Log1p</code> when implementing neural network algorithms, but I&rsquo;m sure you&rsquo;d agree that having a library that automatically converts <code>log(1+x)</code> to use <code>math.Log1p(x)</code> be rather awesome? It reduces the element of human error.</p>
<h3 id="machine-learning-specific-optimizations">Machine-Learning Specific Optimizations</h3>
<p>Consider a variant of the first program in this post:</p>
<pre><code class="language-go">func a() int {
return 1 + 1
}
</code></pre>
<p>This is the same program compiled down to assembly:</p>
<pre><code>&quot;&quot;.a t=1 size=10 args=0x8 locals=0x0
(main.go:5) TEXT &quot;&quot;.a(SB), $0-8
(main.go:5) FUNCDATA $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
(main.go:5) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
(main.go:6) MOVQ $2, &quot;&quot;.~r0+8(FP)
(main.go:6) RET
</code></pre>
<p>In particular, pay attention to the second last line: <code>MOVQ $2, &quot;&quot;.~r0+8(FP)</code>. The function has been optimized in such a way that <code>2</code> is returned. No addition operation will be performed at run time. This is because the compiler knows, <em>at compile time</em>, that 1 + 1 = 2. By replacing the expression with a constant, the compiler is saving on computation cycles at run time. If you&rsquo;re interested in building compilers, this is known as <a href="https://en.wikipedia.org/wiki/Constant_folding" target="_blank">constant folding</a>.</p>
<p>So, we&rsquo;ve established that compilers are smart enough to do optimizations. But the Go compiler (and in fact most non-machine-learning specific compilers) isn&rsquo;t smart enough to handle values that are used for machine learning. For machine learning, we frequently use array-based values, like a slice of <code>float64</code>s, or a matrix of <code>float32</code>s.</p>
<p>Imagine if you will, if you&rsquo;re not doing <code>1 + 1</code>. Instead you&rsquo;re doing <code>[]int{1, 1, 1} + []int{1,1,1}</code>. The compiler wouldn&rsquo;t be able to optimize this and just replace it with <code>[]int{2, 2, 2}</code>. But building a graph object that can be optimized allows users to do just that. Gorgonia currently doesn&rsquo;t do constant folding yet (earlier versions had constant folding but it is quite difficult to get right), but it comes with other forms of graph optimizations like <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination" target="_blank">common expression elimination</a>, some amount of variable elimination and some minimal form of tree shaking. Other more mature libraries like TensorFlow or Theano comes with very many optimization algorithms for their graphs.</p>
<p>Again, one could argue that this could be done by hand, and coding it into the program would be more than doable. But is this really where you&rsquo;d rather spend your time and effort? Or would you rather be creating the coolest new deep learning stuff?</p>