<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Tomas Reimers on Medium]]></title>
        <description><![CDATA[Stories by Tomas Reimers on Medium]]></description>
        <link>https://medium.com/@tomasreimers?source=rss-92483908161b------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*Jz-xU7qMXPpdTAJbIwLAkQ.jpeg</url>
            <title>Stories by Tomas Reimers on Medium</title>
            <link>https://medium.com/@tomasreimers?source=rss-92483908161b------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 04:29:02 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@tomasreimers/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Stacked changes: how Facebook and Google engineers stay unblocked and ship faster]]></title>
            <link>https://medium.com/@tomasreimers/stacked-changes-how-facebook-and-google-engineers-stay-unblocked-and-ship-faster-63027d19d56a?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/63027d19d56a</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[stacked-changes]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[google]]></category>
            <category><![CDATA[facebook]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Wed, 17 Nov 2021 16:35:52 GMT</pubDate>
            <atom:updated>2021-11-17T16:36:30.826Z</atom:updated>
            <content:encoded><![CDATA[<p><em>(Originally @ </em><a href="https://graphite.dev/blog/post/DThX8ffP1gmxWJChEv0y"><em>https://graphite.dev/blog/post/DThX8ffP1gmxWJChEv0y</em></a><em>)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/0*GRr-RO8MyG1Fc8th.jpg" /></figure><p><em>POV: waiting for your pull requests to get reviewed</em></p><p>You just finished writing the new comments feature for your web app: you wrote the code, tested it, and put up the PR. You’re under a deadline and your next highest priority task is to add reactions to those comments.</p><p>But wait a minute — the reactions code will depend on the comments code, which isn’t yet merged to main. <strong>Which means you&#39;re blocked.</strong></p><p>If you’ve ever found yourself in this position, you know you have three options:</p><ol><li>Work on something lower-priority while you wait for your comments PR to be approved (and risk missing the deadline).</li><li>Interrupt your teammate to ask for a faster review.</li><li>Make your comments PR bigger by merging what should have been a separate reactions PR into it (which will make it harder to review and increase the chance of bugs).</li></ol><h3>Stacking your changes</h3><p>“But what about the fourth option?” — you might ask. Why can’t you write the reactions branch on top of the comments branch to stay unblocked without compromising on the size of your PRs?</p><blockquote><strong><em>The blocker here isn’t Git. In fact, the reason you can’t go with option #4 is that your code review tools probably don’t support it:</em></strong></blockquote><ul><li>How do you tell reviewers that your reactions PR depends on the comments PR?</li><li>How will reviewers see the relevant diff for the reactions PR (excluding the comments PR changes)?</li><li>How can you propagate changes to the reactions PR if you ever need to update the comments PR before you land it (i.e. to address review comments)?</li></ul><p>But suppose we did have the right tooling to make all of this easy — would you stop at just 2 PRs stacked on top of each other? Should comments really be just one PR? Or would you break it into the series of atomic changes you originally wrote: a DB change, a new endpoint to the API schema, the implementation of that endpoint, and the front-end changes that call that endpoint?</p><h3>No one likes a 4,000-line pull request</h3><p>I think most developers agree that smaller PRs are easier to work with:</p><h3>I Am Devloper on Twitter: &quot;10 lines of code = 10 issues.500 lines of code = &quot;looks fine.&quot;Code reviews. / Twitter&quot;</h3><p>10 lines of code = 10 issues.500 lines of code = &quot;looks fine.&quot;Code reviews.</p><p>Smaller changes help you get faster, more thorough reviews on your code, be more targeted in who you tag (why do backend engineers and designers both need to review the same change?), and experience fewer merge conflicts.</p><p><strong>By stacking your changes, you can get all of these benefits while staying unblocked and not needing to thrash your teammates.</strong></p><h3>Introducing Graphite</h3><p>This idea isn’t new, and if you’ve worked at a company like Facebook, Google, Uber, Dropbox, or Lyft (among others), you’ve either had first-class support for stacked changes built into your code review platform (i.e. Phabricator at Facebook, Critique at Google) or scripts built on top of it to enable stacking. Many engineers who come from those companies seem to miss this workflow, but it has yet to make its way into the public toolchain.</p><h3>Dan on Twitter: &quot;I like GitHub announcing new features but really all we need is Stacked Diffs. Please? / Twitter&quot;</h3><p>I like GitHub announcing new features but really all we need is Stacked Diffs. Please?</p><h3>vjeux ✪ on Twitter: &quot;Stacked diffs are the biggest feature I&#39;d love to see support added on github! https://t.co/sijkqhsGLc / Twitter&quot;</h3><p>Stacked diffs are the biggest feature I&#39;d love to see support added on github! https://t.co/sijkqhsGLc</p><h3>Alexandra on Twitter: &quot;The experience I probably miss most from writing code at FB is stacked diffs. It was SO easy to split diffs/PRs and have &quot;1 idea&quot; per change set. I has no clue this was not a widely available feature in industry. / Twitter&quot;</h3><p>The experience I probably miss most from writing code at FB is stacked diffs. It was SO easy to split diffs/PRs and have &quot;1 idea&quot; per change set. I has no clue this was not a widely available feature in industry.</p><p><strong>We’re among the engineers who want stacked changes back — so much so that we’re building the tooling to make it accessible for everyone else.</strong></p><p><a href="https://graphite.dev/blog/post/pF8IehgS5rF64tmVz6NA"><strong>Graphite</strong></a> — our platform for stacked changes — is <a href="https://graphite.dev/blog/post/y6ysWaplagKc8YEFzYfr">built directly on top of Git</a> and seamlessly syncs with GitHub. <strong>You can use it without needing anyone else on your team to change their workflows</strong> — they’ll see normal PRs from you in GitHub, which they can comment on and review as they always have.</p><p>Graphite is currently in closed beta, but it’s already used by engineers at some of the largest companies every day. If you’re interested in being among the first to try it out, you can <a href="https://graphite.dev/"><strong>sign up for our waitlist starting today</strong></a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*o4xsB-baPmCy1Erl" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=63027d19d56a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Compiling TensorFlow for the Browser]]></title>
            <link>https://medium.com/@tomasreimers/compiling-tensorflow-for-the-browser-f3387b8e1e1c?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/f3387b8e1e1c</guid>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[emscripten]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[compilers]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Mon, 02 Apr 2018 12:38:46 GMT</pubDate>
            <atom:updated>2018-04-02T12:38:46.358Z</atom:updated>
            <content:encoded><![CDATA[<h4>Another Approach for Running Machine Learning in JavaScript</h4><blockquote><strong>tl;dr</strong> This post summarizes and discusses part of my undergraduate senior thesis (a research project that took place between September 2016 and March 2017): compiling Google’s TensorFlow library into JavaScript.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-ZSKf9rP73SL1-fPNGMToA.png" /><figcaption>Demos at: <a href="https://tensorflowjs.github.io/">https://tensorflowjs.github.io/</a></figcaption></figure><p>In case you missed it, Google just announced <a href="https://medium.com/tensorflow/introducing-tensorflow-js-machine-learning-in-javascript-bf3eab376db">TensorFlow.js</a>: a largely <a href="https://github.com/tensorflow/tfjs-core">hand-ported</a> version of TensorFlow to JavaScript. This is super exciting for me for two reasons:</p><ol><li>Generally speaking: I think it is a super interesting, and difficult, problem, so it’s really cool to see a big company tackling it and releasing their work.</li><li>Closer to home for me: almost exactly a year ago (March 31st, 2017), I handed in my undergraduate senior thesis: <em>Machine Learning in the Browser</em>. It was an overview of what was possible, theoretical blockers, and a largely focused on my efforts <em>compiling</em> TensorFlow into JavaScript (which I, confusingly, also called TensorFlow.js). Needless to say, this is a space I care for and have thought about a lot.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5EY5WpfzEMpQOUpROjQUcw.png" /><figcaption>Running Inception in the Browser. <a href="https://tensorflowjs.github.io/">https://tensorflowjs.github.io/</a></figcaption></figure><p>I’ll start off by saying my work is not production-ready and is still very much a research project; however, given Google’s announcement, I wanted to pause and share what I had found, in hopes that it might motivate or inform someone else’s work. I was hoping I would be able to share all of this after I had had better luck compiling XLA output to JS (more on that later); however, some combination of it being a hard problem and life happening (graduating / moving to a new city / starting a new job), prevented me from making much progress, so there is still much work to be done.</p><blockquote>Note: Almost all of this work is a year old, meaning the TensorFlow I compiled is pinned at version 1.0.0 (which was the most recent version when I submitted my thesis). If there is enough interest, I can look into bumping the version; when I was actively working on this, I bumped from 0.10 to 0.11, 0.11 to 0.12, and 0.12 to 1.0 all without any major issues.</blockquote><h3>Why would anyone want this?</h3><p>When I was doing my thesis there were two questions I would commonly get: how is this research, and not just an engineering problem? (To which I still don’t have a good answer.) And why would anyone want to do machine learning (and specifically inference) in the browser? To me, it seemed like the obvious fusion of two trends: machine learning becoming ubiquitous and web applications becoming more powerful; concretely:</p><p>(Reason #1) <strong>Privacy Guarantees: </strong>This is fairly straight-forward, people want to be able to have the advantages of machine learning such as image classification, hot-word activation for speech recognition (e.g. “Ok Google…” or “Alexa…”), and smart reply to messages without needing to give up their raw data to companies. On the flip side, companies don’t always want user data, in a lot of cases it can pose a liability for them to be storing this data on their servers.</p><p>(Reason #2) <strong>Offline Mode: </strong>A lot of web apps are not supporting some <a href="https://developer.mozilla.org/en-US/Apps/Fundamentals/Offline">limited, offline version of their application</a>. It would be nice if these offline modes weren’t totally unable to run any ML at all.</p><p>(Reason #3) <strong>Free / Local Computation: </strong>Imagine a world where every ML model open-sourced on github came with a web demo where you could input an image to classify, or upload your own test set to see how it performs. Right now this doesn’t happen because no one wants to pay for the compute. With the capacity to run ML in the browser, no one would have to. More generally, this would enable a class of <a href="https://unhosted.org/">unhosted</a> web applications to use machine learning.</p><p>Note: It is important to call out that: (1) this is not about running TensorFlow in JavaScript for some node-like environment. TensorFlow is a C++ core with bindings to many languages (Python being the most popular), so for that we could simply create a JS binding to the core; (2) I think inference is a much more valuable use case than training (with the exception of transfer learning to fine-tune models, I can’t imagine many usecases for full-scale ML model training in the browser). This is about being able to deliver and run machine learning models to people’s browsers.</p><h3>Approaches</h3><p>As far as I’m concerned, there are two real ways to get TensorFlow running in the browser: rewriting it (which would take a massive engineering force, something that large companies have at their disposal, but individual developers / students doing an undergraduate thesis do not) or compiling it. <a href="https://github.com/dmlc/mxnet.js/">MXNet.js</a> is an example of a major machine learning library being compiled into JS; Google’s <a href="https://js.tensorflow.org/">TensorFlow.js</a> / <a href="https://transcranial.github.io/keras-js/">Keras.js</a> / <a href="https://tenso.rs/">Tensor Fire</a> are examples of handwritten implementations.</p><p>Lack of Google-scale engineering resources aside — and even with a working handwritten port of TensorFlow in JS — compiling TensorFlow to JS seems like an approach worth exploring for 3 major reasons:</p><p>(1) <a href="https://github.com/tensorflow/tensorflow/graphs/code-frequency">TensorFlow is rapidly changing</a>, and having to constantly port it by hand seems like it will take a massive, sustained engineering effort.</p><p>(2) The Web is rapidly changing. Since the submission of my thesis and writing this blog post (almost exactly a year apart) the standards for the web are already different. APIs I was excited for have been deprecated, and new technologies have been proposed. Compiling it would abstract away these changes.</p><p>(3) <a href="http://webassembly.org/">Compiled JavaScript</a> has some speed benefits over hand-written JavaScript (which is critical for performance-sensitive things, like machine learning), but more on this later.</p><p>Compiling C++ into JavaScript isn’t actually as <a href="https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript">crazy as it seems</a>. <a href="https://github.com/kripken/emscripten">Emscripten</a> is an open-source project developed to compile JavaScript into <a href="http://asmjs.org/">ASM.js</a>. ASM.js is a subset of javascript meant to be used as a compile target. Some browsers even fast-track the execution of asm code (by compiling it into machine instructions), allowing it to run at only <a href="http://kripken.github.io/mloc_emscripten_talk/#/28">2x native speed</a>, even for large benchmarks.</p><h3>A primer on TensorFlow</h3><blockquote>Note: This writing is largely distilled from my research — which is almost a year old — so the following may be a little outdated.</blockquote><p>TensorFlow — along with almost every machine learning framework — faces a tradeoff between needing a high-level language and needing speed. Without a high-level language, it is difficult to architect complex models and express abstract concepts in a readable way. Without speed, it is difficult to run the actual neural nets. TensorFlow — along with many other libraries — reconciles these opposing requirements by allowing developers to construct a computation graph in Python, and sending the graph along with any inputs to a C++ core to evaluate. (While Python is the most common front-end to TensorFlow’s core, the <a href="http://www.swig.org/">SWIG</a> bindings allow for TensorFlow bindings in <a href="https://www.tensorflow.org/api_docs/java/reference/org/tensorflow/package-summary">many languages</a> that all interface back to the same common core.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jlQWTgwqDMp5YjGJZV-34w.png" /></figure><p>A TensorFlow graph consists of Ops (nodes) and Tensors (edges), imagine the following TensorFlow program:</p><pre>import tensorflow as tf</pre><pre># (<strong>Part 1</strong>) create graph<br>y = tf.add(<br>    tf.multiply(<br>        tf.Variable(2, name=&quot;m&quot;), # initial value of 2<br>        tf.placeholder(tf.int32, shape=(1,), name=&quot;x&quot;)<br>    ),<br>    tf.Variable(1, name=&quot;b&quot;) # initial value of 1<br>)</pre><pre># (<strong>Part 2</strong>) create and initialize session <br>sess = tf.Session()<br>sess.run(tf.global_variables_initializer()) # sets m and b</pre><pre># (<strong>Part 3</strong>) run inputs through the graph<br>print sess.run(&quot;Add:0&quot;, feed_dict={&quot;x:0&quot;: (5,)}) # returns 11</pre><p>Part 1 in the code creates a TensorFlow graph with the following shape:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*8Z2l5-7wHo60eXDT6z6PKA.png" /></figure><p>As you can see, it has both ops (the circles) and tensors (the edges). Tensors are simply multi-dimensional arrays (a rank-0 tensor is a scalar, a rank-1 tensor is a vector, a rank-2 tensor is a matrix, etc). Ops take in 0 or more tensors and produce 0 or more tensors. For example, the add op takes in 2 tensors, and produces 1 tensor. The names for tensors are op_that_produced_them:index. For example, the result of the add operator is denoted as “add:0”.</p><p>Part 2 (in the code) loads this graph in the C++ core, and part 3 says fetch the value for add:0, initializing (or “feeding”) the tensor x:0 with a value of 5. The first op that can be evaluated in the graph is “multiply”, as all of its inputs are defined (m:0 and x:0). After evaluating multiply, multiply:0 is set to the result of the op. With multiply:0 set, all of add’s inputs are defined so it is evaluated next, which sets add:0 and returns it to the user.</p><p>TensorFlow compute graphs can be serialized and distributed as <a href="https://developers.google.com/protocol-buffers/">Protobufs</a> (a serialization format developed and used extensively by Google). A common approach for deep learning in TensorFlow is to architect a graph in python, train it (i.e. set the variables in the graph), export that graph along with values for the variables to protobufs, and distribute those protobufs to servers or mobile devices.</p><h3>A primer on Emscripten</h3><p>TensorFlow is capable of running on many devices architectures (i.e. <a href="https://en.wikipedia.org/wiki/X86-64">x86</a> on your laptop and <a href="https://en.wikipedia.org/wiki/ARM_architecture">ARM</a> on your smartphone) through the magic of compilers. TensorFlow’s source code (and, specifically, the C++ core) can be compiled into machine-specific instructions for a variety of compile targets.</p><p>In broad strokes, compilers work by taking source code, “lowering” it to an intermediate representation (that is somewhere between source code and machine code), optimizing the intermediate representation, and then lowering that into machine code. For example, the popular compiler Clang compiles C++, by lowering it into LLVM-IR (the intermediate representation), optimizing that, and then lowering it into machine code. The first part of this process (source code to LLVM-IR) is referred to as the front end of the compiler, and the latter (LLVM-IR to machine code) is the backend of the compiler. Emscriten works by providing an alternative backend to Clang, and compiling the LLVM-IR to <a href="http://asmjs.org/">ASM.js</a> (a subset of JS intended as a compile target).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_S4_AKRXcyoGInv_WKYg4Q.png" /></figure><p>Recently, web standards have even gone further in defining a good compile target. <a href="http://webassembly.org/">WebAssembly</a> (WASM) is a binary protocol that is supported by every major browser (albeit behind a flag in some of them) that’s intended to be <em>the</em> compile target for the web. Because WASM is a binary protocol defined from scratch, it is smaller and can run faster than ASM.js. Emscripten can optionally generate WASM from C++ targets, simply bypassing a flag to the compiler.</p><h3>Coercing TensorFlow to Compile</h3><p>Even using Emscripten, there are still challenges to compiling TensorFlow. The first challenge I encountered was literally using Emscripten. Emscripten is invoked as a drop-in replacement for gcc, <a href="https://kripken.github.io/emscripten-site/docs/getting_started/Tutorial.html">emcc</a>. It is also distributed with two tools, <a href="https://kripken.github.io/emscripten-site/docs/compiling/Building-Projects.html#building-projects">emmake and emconfigure</a>, to make it easier to integrate with pre-existing makefile build systems. TensorFlow doesn’t use a makefile. Rather, it uses Google’s open-sourced build tool, <a href="https://bazel.build/">Bazel</a>. I explored updating the bazel files to use Emscripten, but soon realized that (1) the bazel <a href="https://github.com/bazelbuild/bazel/wiki/Building-with-a-custom-toolchain">crosstool functions</a> are largely undocumented (or poorly documented); (2) the requisite files would effectively require maintaining a separate build system that would require significant upkeep.</p><p>Fortunately, TensorFlow maintains a <a href="https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/makefile">makefile</a> for use with their mobile framework. One of the themes of this project is that in many ways compiling for JavaScript is similar to compiling for mobile (as resources and computational primitives are limited). By modifying the mobile makefile, we we embarked on a journey of debugging C++ compiled into a JS binary. That means we had few (read no) debugging tools, and limited insight into what is happening in the binary.</p><p>The first hurdle was that TensorFlow’s dependencies must be compiled into JavaScript. At the time, there are three critical dependencies for TensorFlow’s core: libmath, zlip, and libprotobuf. Because libmath is ubiquitous, it ships with Emscripten. ZLib has <a href="https://github.com/emscripten-ports/zlib">already been ported to Emscripten</a> (although we discovered this after we already ported it). LibProtobuf had <a href="https://github.com/invokr/protobuf-emscripten">prior work</a> in compiling it to JavaScript, and so all we had to do was <a href="https://github.com/tomasreimers/protobuf-emscripten">fork the repository</a> and bump the version (dealing with minor bugs). If you want to use the image ops (e.g. DecodeJPEG) you also need to compile JPEG, PNG, and GIF libraries. I have done this, but exclude it from here for the sake of brevity (feel free to contact me for more details if this is interesting).</p><p>The next issue was a type error, and to resolve it we would have to make TensorFlow work on 32-bit systems. Because all JavaScript numbers are implemented as <a href="http://www.ecma-international.org/ecma-262/8.0/index.html#sec-ecmascript-language-types-number-type">IEEE754 double precision floating points</a> (Section 6.1.6), they only have 53 bits of integer precision. This means that Emscripten can only reliably emulate 32-bit systems (as it could not address into a full 64-bit address-space). This is problematic because TensorFlow only supports 32-bit systems. Fortunately, I only had to make minor modifications to correct this.</p><p>TensorFlow uses <a href="http://eigen.tuxfamily.org/index.php?title=Main_Page">Eigen</a> as it’s linear algebra library — As an aside, this was incredibly lucky for me because many popular linear algebra libraries are written in Fortran (<a href="http://www.netlib.org/blas/">Blas</a>, <a href="http://www.netlib.org/lapack/">Lapack</a>, <a href="http://math-atlas.sourceforge.net/">Atlas</a>) and could not be easily compiled with Emscripten — Eigen <a href="https://github.com/RLovelett/eigen/blob/f14463caef30220c1bc65510e3103d170908fefb/Eigen/src/Core/util/Meta.h#L25">defines scalars</a> to be of <a href="https://github.com/RLovelett/eigen/blob/c357eb377661d0674e5bc1acab48a4957d3d671f/Eigen/src/Core/util/Macros.h#L336">type std::ptrdiff_t</a>. The problem is that it is common in the TensorFlow code base to do something akin to the following:</p><pre>int64 a = 5;</pre><pre>// ...other stuff happens...</pre><pre>Eigen::Index b = a;</pre><p>This will cause the compiler to (rightly) complain about type narrowing, because in JS Eigen::Index is only an int32. In order to fix this, we simply need to replace instances of int64 with the more correct “Eigen::Index”. This was a surprisingly small amount of changes.</p><p>At this point, TensorFlow will compile. It will just hang when it is run. This is because JavaScript currently has no way to create POSIX-style threads (although there are experiments to create <a href="https://kripken.github.io/emscripten-site/docs/porting/pthreads.html">posix-style threads using webworkers with a shared memory array in FireFox nightly</a>). Vastly oversimplifying some details, the way in which TensorFlow processes graphs is by creating a threadpool and dispatching op kernels (the implementation of the ops) to other threads. On Emscripten, no threadpool is created, and the main thread is left waiting for a task that will never complete. To correct this, I had to dive into the concurrency model. TensorFlow’s scheduler effectively takes a function closure and dispatches it to a thread, so to correct this, simply replace the scheduler with a function that executes the closure immediately and on the current thread. This ensures that by the time any thread barrier is invoked all prerequisite tasks have already been completed.</p><p>TensorFlow will now compile and run in JavaScript.</p><h3>DevX</h3><p>This creates a JavaScript library that we can link other Emscripten-compiled C/C++ programs against. The problem is that I don’t believe developers want to write their machine learning for the web in C/C++, they tend to avoid it even for native programs. Taking some inspiration from the python frontend, my goal was to create a library that could take in a graph file, and return a class that the developer could pass inputs into and get outputs from.</p><p>This library would require a C++ component to actually use the compiled TensorFlow library, and a JavaScript component to wrap the C++ component and pass in inputs. Emscripten allows one to expose and call functions in the C++ using <a href="https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html">embind</a>. Embind is incredibly powerful, but only really supports simple argument types. In order to pass graphs and input tensors to the C++ library, I needed to someway to serialize them into basic types. Fortunately, TensorFlow defines a <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/tensor.proto">Protobuf to serialize tensors</a>! This meant that I could serialize the tensor in JavaScript, convert it to a string, pass it into the C++ layer (through embind), and deserialize the Tensor. I wrote <a href="https://www.npmjs.com/package/tensorjs">a library to create tensors from multidimensional javascript arrays</a>.</p><p>I then wrote <a href="https://www.npmjs.com/package/tfjs">the wrapper that loaded in the compiled C++ core and provided convenience methods to invoke it</a>. For example, to run the simple y=mx+b graph from above:</p><pre>const TFJS = require(&#39;tfjs&#39;);<br>const tensorjs = require(&#39;tensorjs&#39;);</pre><pre>// load in the graph protobuf, maybe through a string<br>// const example_graph_pb = ...</pre><pre>TFJS.for_browser(&#39;/tensorflowjs/&#39;).then(lib =&gt; {<br>  const sess = new lib.Session(example_graph_pb);<br>  const results = sess.run(<br>    {<br>      &quot;x:0&quot;: tensorjs.intTensor([5]),<br>    },<br>    [&quot;add:0&quot;]<br>  );</pre><pre>console.log(results[0]); // prints 11<br>});</pre><p>On top of that, I wrote a quick library to make encoding images as tensors easier:</p><pre>const TFJS = require(‘tfjs’);<br>const tensorjs = require(‘tensorjs’);</pre><pre>// const mnist_graph_pb = ...</pre><pre>TFJS.for_browser(‘/tensorflowjs/’)<br>  .then(lib =&gt; {<br>    const sess = new lib.Session(mnist_graph_pb);</pre><pre>// pull out handwriting image from a canvas<br>    const canvas = document.getElementById(‘handwriting’);<br>    const context = canvas.getContext(‘2d’);<br>    const img_data = context.getImageData(0, 0, canvas.width, canvas.height);<br>    const img_array = lib.image_ops.get_array(img_data, true, 0, 255);</pre><pre>const results = sess.run(<br>      {<br>        &quot;Reshape:0&quot;: tensorjs.floatTensor(img_array),<br>        &quot;dropout:0&quot;: tensorjs.floatTensor(1.0)<br>      },<br>      [&quot;prediction_onehot:0&quot;]<br>    );<br>  });</pre><h3>Benchmarks</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DKfdau9qglw8kEr_LupsdA.png" /><figcaption>Average runtimes in seconds over 100 trials running on a Macbook Pro 2015 with an 2.9GHz i5 processor. The number in parenthesis represents the ratio when compared to singlethreaded C.</figcaption></figure><p>Given my naive approach, I did not expect to be fast; however, our JS execution times were within an order of magnitude of singlethreaded, CPU-only TensorFlow. For running full TensorFlow graphs in the web browser, this seems quite fast, and it backs up Emscripten’s claim that we might be able to get native code running in the web browser at near native speeds (something that always make me think on <a href="https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript">The Birth and Death of JavaScript</a>). I expect these metrics will continue to improve as WASM becomes more powerful.</p><p>While speed was good, size was not. The library was 30MiB, with another 4.7MiB needed to initialize Emscripten. Compressed they were better (4.2MiB library, and 408KiB to initialize), but still large for web libraries. We can improve this by <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/print_selective_registration_header.py">selectively including op kernels in the compiled code</a> (op kernels are the code to necessary to evaluate nodes in the computation graphs); for context, at the time, TF defined 216 unique ops, and Inception v3 only took 11 to run. However, even if we were to improve the size of the runtime, model graphs can still be 100s of MiB (which would be unreasonable to ship to a web browser); while we can make that better with tools like <a href="https://hackernoon.com/when-smallers-better-4b54cedc3402">quantization</a>, we still have a way to go before we can use that in production.</p><h3>Takeaways</h3><p>Firstly, one of the advantages of waiting almost a year to publish this is that I have the advantage of hindsight. It’s funny reading the discussion section of my thesis to see a lot of predictions I made either come true or fall flat on their face. It does seem to be the case that people want machine learning libraries in the browser, something that was non-obvious when I was doing this. It also seems to be the case that the web is only continuing to get faster (<a href="http://webassembly.org/">WASM has continued support</a>, and we have working groups for better use of the <a href="https://www.khronos.org/webcl/">GPUs </a>and other forms of hardware acceleration on the web).</p><p>Secondly, I’m starting to think that for the time being, compiling an <em>entire</em> machine learning runtime to the web is unfeasible. It is likely the case that we will have breakthroughs that enable this in the future, but for now we should probably direct our efforts elsewhere.</p><p>However, <strong>I don’t think this means that we should discount looking further into compilation as an approach; if anything, it shows that it’s possible</strong>. One of the experiments I’ve been most interested to try would be to take <a href="https://www.tensorflow.org/performance/xla/">Google’s XLA</a> (a compiler from TensorFlow graph to bytecode) and make it compile the graph to JS. This is possible because <a href="https://github.com/tensorflow/tensorflow/blob/1e67c90e2caceeff82d09793d1ef5fa0300d219b/tensorflow/compiler/xla/service/cpu/compiler_functor.cc#L71">XLA uses LLVM-IR as an intermediate step</a>, and, as you may remember, Emscripten compiles LLVM-IR to JS. Like XLA programs in general, these compiled-js programs might be faster and lighter-weight than the full runtime.</p><p>If you’re interested in this work, and want to talk about it or pick up where I left off, feel free to reach out! I would love to chat.</p><h3>Resources</h3><p><strong>Website with Working Examples:</strong> <a href="https://tensorflowjs.github.io/">https://tensorflowjs.github.io/</a></p><p><strong>Repositories:</strong></p><ul><li>Where I did most my work: <a href="https://github.com/tomasreimers/tensorflow-emscripten">https://github.com/tomasreimers/tensorflow-emscripten</a></li><li>Compiled TensorFlow: <a href="https://github.com/tomasreimers/tensorflowjs">https://github.com/tomasreimers/tensorflowjs</a></li><li>Wrapper to interface with the compiled assets: <a href="https://github.com/tomasreimers/tfjs">https://github.com/tomasreimers/tfjs</a></li><li>Library to read and write tensors to protobufs (in JS): <a href="https://github.com/tomasreimers/tensorjs">https://github.com/tomasreimers/tensorjs</a></li></ul><p><em>P.s. If there is anyone from Google reading this, congrats on the project launch!! It’s really cool! Also, if you would like the tensorflowjs github organization name —I would be happy to transfer ownership; you seem to have a better use for it than I do now :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f3387b8e1e1c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Axolotl: A Keylogger for iPhone and Android]]></title>
            <link>https://medium.com/@tomasreimers/axolotl-a-keylogger-for-iphone-and-android-a8b7b62cdab4?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/a8b7b62cdab4</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[mobile]]></category>
            <category><![CDATA[data-visualization]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Mon, 28 Aug 2017 17:27:56 GMT</pubDate>
            <atom:updated>2017-08-28T18:11:10.462Z</atom:updated>
            <content:encoded><![CDATA[<h4>A Guide to Reasoning About Unintuitive Machine-Learning Problems</h4><p><em>Note: This post was co-authored by </em><a href="https://medium.com/@gregorymfoster"><em>Greg Foster</em></a><em> (Medium won’t let us add co-authors), definitely check out his profile!</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*suu4Zt34M0uj2JLpTb7RVw.png" /><figcaption>Green is the ground truth touch on an iPhone screen, red is where our algorithm predicts the touch occurred based solely on accelerometer and gyroscope data (which can be read by apps in the background).</figcaption></figure><p><strong>TL;DR</strong> This post motivates and describes an attack where accelerometer/gyroscope readings and machine learning are used to develop a keylogger for mobile devices. While <a href="https://www.usenix.org/legacy/event/hotsec11/tech/final_files/Cai.pdf">previous</a> <a href="http://web.cs.ucdavis.edu/~hchen/paper/trust2012touch.pdf">research</a> has been conducted <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.294.9887&amp;rep=rep1&amp;type=pdf">in this</a> <a href="http://www.hotmobile.org/2012/papers/HotMobile12-final42.pdf">space</a>, we hope that our narrative is useful for someone tackling an unintuitive machine learning problem (also the results and graphs are just really darn cool).</p><h3>Hypothesis Formation</h3><p>In Fall 2016, we were tasked with creating a final project for <a href="https://mickens.seas.harvard.edu/classes/cs-263-systems-security">CS263</a> (Harvard’s Systems’ Security Class): implementing <em>some</em> attack. Mostly to spite our hype-hating professor, we committed to integrating the greatest buzzword of all into our project — machine learning. We had been intrigued by <a href="http://ieeexplore.ieee.org/document/1301311/">previous</a> <a href="https://people.eecs.berkeley.edu/~tygar/papers/Keyboard_Acoustic_Emanations_Revisited/TISSEC.pdf">attacks</a> that used machine learning on audio data of keyboards and pin pads to extract passwords. This type of attack is called a <a href="https://en.m.wikipedia.org/wiki/Side-channel_attack">side channel attack</a> and takes advantage of the physical properties of the real-world implementation of a security system. Side-channel attacks seem like good candidates for machine learning because they often involve picking up on subtle, non-evident patterns. We decided to devise a side-channel attack for mobile devices, which are chock-full of incredibly-accurate, and permission-less, sensors. Specifically, we wanted to develop an attack that exploited the <a href="https://en.wikipedia.org/wiki/Accelerometer">accelerometer</a> (measures movement) and <a href="https://en.wikipedia.org/wiki/Gyroscope">gyroscope</a> (measures rotation), as they don’t require permissions and can be read by apps running in the background. On iPhone, apps can run for up to 10 minutes in the background after they’ve been closed.</p><p>After a bit of brainstorming, we formed our 4-part hypothesis:</p><p>(1) When you tap your phone, it moves.<br>(2) This movement can be picked up by the gyroscope and accelerometer.<br>(3) This movement is distinct from other movement and can be identified by machine learning and statistical techniques.<br>(4) The movement is sufficiently unique to identify where on-screen it occurred.</p><p>While we were fairly certain about (1) and (2), we were not sure about either (3) or (4). <a href="https://www.usenix.org/legacy/event/hotsec11/tech/final_files/Cai.pdf">Previous</a> <a href="http://web.cs.ucdavis.edu/~hchen/paper/trust2012touch.pdf">work</a> <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.294.9887&amp;rep=rep1&amp;type=pdf">in this</a> <a href="http://www.hotmobile.org/2012/papers/HotMobile12-final42.pdf">space</a> quickly lead us to believe it was possible, but we weren’t sure how robust these methods were or if we’d be able to recreate them. Generally, we’ve learned to be weary with academic descriptions of attack vectors — as sometimes they only work in a lab setting, expect certain conditions, or simply aren’t as practical as their authors make them out to be (of course, this does not describe all papers — but there are certainly a good number for which this is the case).</p><h3>Evil: Predicting Touches</h3><p><em>Repository here: </em><a href="https://github.com/tomasreimers/axolotl"><em>https://github.com/tomasreimers/axolotl</em></a></p><p>With <em>any </em>deep learning problem, the first question you need to ask yourselves is what data do you care about. Neural nets are very specific devices that learn mappings from some well-defined input to some well-defined output. They do this by learning statistical correlations between labeled inputs and outputs. Many people, especially those newer to machine learning, seem to think that stellar algorithms are where people really differentiate themselves. While that’s true, a large majority of modern machine learning problems are solved with fairly canonical algorithms, and a really awesome data set.</p><p>On the iPhone, one accesses accelerometer and gyroscope readings by listening to events emitted by iOS at seemingly-random intervals (accelerometer and gyroscope readings are different events, and emitted separately). We developed <a href="https://github.com/tomasreimers/axolotl/tree/master/app/axolotl">an app</a> to continuously log these events along with touch data. We used this code to collect <a href="https://github.com/tomasreimers/axolotl/tree/master/data">8 data sets</a> to experiment on. Our data sets contained rows of data, where each row represented one of the data events, including the unix timestamp it was collected at, whether this data was an accelerometer or gyroscope event, the x/y/z of the data (both accelerometer and gyroscope data are 3 dimensional: accelerometers can move along the x/y/z axis, and gyroscopes have <a href="https://en.wikipedia.org/wiki/Euler_angles">3 degrees of rotational freedom</a>); additionally, we also collected whether the user was touching the screen at the time of the accelerometer / gyroscope event, and, if so, where the touch had started (the beginning of the touch likely started with the user tapping the phone).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VlQfILEjxDGsuLghWtzARg.png" /><figcaption><a href="https://github.com/tomasreimers/axolotl/blob/master/data/sample_0/accel.txt">Source</a>. Note: We normalized locations to be [-1, 1]; -2 is a special value reserved to indicate that no touch was occurring at that time.</figcaption></figure><p>We then graphed the data by time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I0AJXX7CcvFw9IUnH0Iy4w.png" /><figcaption>Source: <a href="https://github.com/tomasreimers/axolotl/blob/master/visualize.py">visualize.py</a>, option #1. Red denotes the duration that the user was actually touching the screen; as you can see from the short touch duration, these are a sequence of taps. The x-axis is time and the y-axis is raw reading from the sensor.</figcaption></figure><p>This was super encouraging because it showed us that taps began with a distinct spike in readings. When doing machine learning on sources where you’re not sure how much signal there is, it is usually a good idea to start with small predictions and get progressively more complex. For this reason, we decided to see whether a neural net could predict whether a randomly sampled 200ms window was a tap or not (from visual inspection, it seemed all the spikes indicating a spike fell within a 200ms span).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6d-cGVVoix6Ik3Zc9N2Muw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kCKzY_n2VlcOJWadklIWqQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R8C4-AyoRlHQxVJkvpw7vQ.png" /><figcaption>Source: <a href="https://github.com/tomasreimers/axolotl/blob/master/visualize.py">visualize.py</a>, option #2–3. Left: raw data. Blue windows indicate tap samples, and green windows indicate not-tap samples. Center: Tap samples. Right: Not-tap samples. As you can see, there is a clear visual difference between tap samples and non-tap samples.</figcaption></figure><p>Traditional neural networks require fixed-sized inputs, and our windows contained variable amounts of data events (emitted at the whim of the operating system). In order to overcome this, we decided to interpolate N points at equal distances in windows. Then we could feed our neural net an input with 6N values (3 gyroscope and 3 accelerometer values for each of the N points). We tried various values for N and ultimately settled on N=20 (the trade-off is more information vs slower training times).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*owbWQDQAsvRIozxF9oABLQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rwszl-4WnQbSQo9DzeTY9g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z2FhPQHFGryT8Wo23F1Nag.png" /><figcaption>Source: <a href="https://github.com/tomasreimers/axolotl/blob/master/visualize.py">visualize.py</a>, option #4–6. Left: N=10. Center: N=20. Right: N=30. The one on the left seems to lose valuable information about the curve, while the middle and right seem fairly similar.</figcaption></figure><p>After <a href="https://xkcd.com/1838/">a little bit of experimenting</a>, we settled on a 5-layer fully-connected neural network with relu activations as our binary classifier. This method leads to the following prediction accuracies on held-out testing sets:</p><p>Sample 0: <strong>97.5% correct</strong><br>Sample 1: <strong>94.7% correct</strong><br>Sample 2: <strong>81.8% correct</strong><br>Sample 3: <strong>96.7% correct</strong><br>Sample 4: <strong>91.2% correct</strong><br>Sample 5: <strong>87.7% correct</strong><br>Sample 6: <strong>83.2% correct</strong><br>Sample 7: <strong>92.6% correct<br></strong><em>Source: </em><a href="https://github.com/tomasreimers/axolotl/blob/master/learn_touches.py"><em>learn_touches.py</em></a></p><p><em>Note: another, possibly “more correct,” way to do this would use an </em><a href="https://en.wikipedia.org/wiki/Recurrent_neural_network"><em>RNN</em></a><em> to analyze the sequence data. We chose not to do that primarily because densely connected neural nets tend to be less expensive to train than RNNs over long streams of data.</em></p><h3>Eviler: Predicting Location</h3><p>Being able to detect taps reliably gave us faith that we might be able to detect the location of the taps on the screen. We modified our neural net to output an X, Y coordinate for the predicted location and retrained it. We got the following mean error (the average distance between our predicted touch location and the true location of the touch).</p><p>Sample 0: <strong>0.366 inches</strong><br>Sample 1: <strong>0.373 inches</strong><br>Sample 2: <strong>0.4077 inches</strong><br>Sample 3: <strong>0.4190 inches</strong><br>Sample 4: <strong>0.4552 inches</strong><br>Sample 5: <strong>0.3935 inches</strong><br>Sample 6: <strong>0.3923 inches</strong><br>Sample 7: <strong>0.3946 inches<br></strong><em>Source: </em><a href="https://github.com/tomasreimers/axolotl/blob/master/learn_location.py"><em>learn_location.py</em></a></p><p>However, as is usually the case with aggregate statistics, the number is only part of the story. When we graph the distribution of errors, the majority are fairly close (with some outliers skewing the mean).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EHhbxOtNaskelNExMUhiJQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*suu4Zt34M0uj2JLpTb7RVw.png" /><figcaption>Source: <a href="https://github.com/tomasreimers/axolotl/blob/master/learn_location.py">learn_location.py</a>. Left: Normalized histogram of error values, as you can see many of the errors are actually very small. Right: Green is the true location of the touch, and red is our model’s predicted location.</figcaption></figure><h3>Evilest: A Unified Pipeline</h3><p>Having these two building blocks, we can now assemble a full app that could key-log. A potential attack vector might look something like this:</p><blockquote>Unsuspecting user downloads “Evil Flappy”, an app where they have to tap on the screen mindlessly to advance some objective. During this tapping, the app uses <a href="http://cs231n.github.io/transfer-learning/">transfer learning</a> to tailor the model to the user and test its own predictive capacity. Once the app detects it has sufficient accuracy, it lets the user win and prompts them to log into Facebook (or any other social network) to share their high score. At this point, the app will be sent to the background, where it can begin key logging the known process: type in a password and press log-in.</blockquote><p>In order to do this, the app collects accelerometer and gyroscope data, and then either analyze it locally or sends it to a server for analysis. Whether it’s happening locally or remotely, a sliding window can be run over the data to detect when taps happen, and, if one did, where that tap occurred.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rBhlRlHlsvFbT2ViV_Z7PA.png" /><figcaption>Source: <a href="https://github.com/tomasreimers/axolotl/blob/master/predict_touches_sequence.py">predict_touches_sequence.py</a> .Top: Raw sensor data. Bottom: The neural net’s prediction of whether there is currently a touch. Red bars indicate the true times of touches.</figcaption></figure><p>The accuracy of this attack could be increased by using a Markov model over what letters might follow other letters in user’s passwords, replacing the neural network to predict letters instead of location (thus giving us the probability it is each individual letter and allowing us to create some kind of Kalman filter over all possible potential passwords), or asking the user to input their password multiple times and building a statistical model to integrate the multiple inputs.</p><p>Some potential fixes that smartphone operating systems manufacturers could implement are: requiring permissions to access the accelerometer / gyroscope, fuzzing the data from those sensors while typing into password sensitive fields, or simply downsampling the rate at which apps can read those sensors while running in the background.</p><h3>General Lessons about Data Science and Machine Learning</h3><p>Throughout this project, we found ourselves grappling with a problem that’s not super intuitive and with too much data to analyze by hand. Lessons learned are:</p><p>(1) Start small, scale up.</p><p>(2) Visualize regularly.</p><p>(3) Aggregate statistics don’t necessarily tell the whole story, make sure you’re choosing correctly.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a8b7b62cdab4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Let’s Encrypt and Google App Engine in 2017]]></title>
            <link>https://medium.com/google-cloud/lets-encrypt-and-google-app-engine-in-2017-7cfe0928768e?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/7cfe0928768e</guid>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[google-app-engine]]></category>
            <category><![CDATA[ssl]]></category>
            <category><![CDATA[lets-encrypt]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Fri, 21 Jul 2017 14:21:16 GMT</pubDate>
            <atom:updated>2017-07-25T17:49:36.727Z</atom:updated>
            <content:encoded><![CDATA[<p>So yesterday I set out to protect my Google App Engine (GAE) API with HTTPS. I had already set up my Google App Engine instance. I was vaguely familiar with Let’s Encrypt (<a href="https://letsencrypt.org/">https://letsencrypt.org/</a>), although I had never used it. I also had a basic grasp of cryptographic primitives used in encryption and certificates.</p><p>There are a lot of blog posts out there about this along with a bunch of StackOverflow posts, all with varying instructions. The official ones by Google seem outdated, and the only thing I could find on Let’s Encrypt side was a bug saying something along the lines of “plz add support for GAE”. I tried a few, and by combining steps in some of them (and a lot of frustration and eventually-resolved confusion) I got it to work. I’m writing up this blog post primarily for myself (so that when I need to re-encrypt in a few months I know how), but hopefully someone out there might find it useful.</p><p><em>(</em><strong><em>Update: </em></strong><em>Automated certificate management is coming soon: </em><a href="https://issuetracker.google.com/issues/35900034"><em>https://issuetracker.google.com/issues/35900034</em></a><em>)</em></p><h4>Preliminaries:</h4><ol><li>LetsEncrypt issues SSL certificates by automatically verifying that you have ownership of the domain you claim you have. (More on how to verify later.)</li><li>LetsEncrypt client has since been renamed to certbot and is maintained by the EFF: <a href="https://certbot.eff.org/">https://certbot.eff.org/</a></li><li>LetsEncrypt allows you to cover up to 100 domains / subdomains in one certificate.</li><li>LetsEncrypt implements a protocol called ACME (Automated Management Certificate Environment) that allows you to perpetually renew their certificates.</li><li>LetsEncrypt only issues 3 month certificates.</li><li>When LetsEncrypt ‘<a href="https://community.letsencrypt.org/t/web-hosting-who-support-lets-encrypt/6920">supports</a>’ a hosting provider, it means you can use their automated software to renew your certificates. If your provider (*cough* Google App Engine *cough*) is not supported, you can still get a LetsEncrypt certificate (it just takes a little more effort), and you’ll need to manually renew it every three months.</li><li>(Not necessary info, but good to know) Amazon apparently has a <a href="https://aws.amazon.com/certificate-manager/">certificate manager</a> that automates this whole process (and renewal) for you.</li></ol><h4>Instructions:</h4><ol><li>Install CertBot, I used (running on Mac OSX):</li></ol><pre>brew install certbot</pre><p>Although there are many ways to install it: <a href="https://certbot.eff.org/docs/install.html">https://certbot.eff.org/docs/install.html</a></p><p>2. Decide whether you want to verify your domain through DNS (adding a TXT record to your DNS, same thing you do to verify ownership for Google Analytics or Google WebMaster tools) or HTTP (uploading a file to your app).</p><p><strong>For HTTP:</strong></p><pre>sudo certbot certonly --manual</pre><p><strong>For DNS (note: DNS can take a while to update, so this may be slower than HTTP):</strong></p><pre>sudo certbot certonly --manual --preferred-challenges dns</pre><p>3. Follow instructions! CertBot will ask you to enter a list of space or comma separated domains. Then it will go one by one asking you to verify them by the method you chose in the previous step (DO NOT close the terminal while you complete these steps, then it will issue a new challenge).</p><p><strong>For HTTP:</strong></p><p>It will ask you to create a file at yourdomain.com/.well-known/acme-challenge/ABC with the value XYZ (note: ABC and XYZ are just placeholders, not actual values). Create folder called letsencrypt in your google app directory, place a file in it called ABC with the contents XYZ, and add the following handler to your app.yaml:</p><pre>handlers:<br>- url: /.well-known/acme-challenge<br>  static_dir: letsencrypt</pre><pre>...</pre><p>Then deploy the app (gcloud app deploy app.yaml)! Finally verify that it works (by visiting yourdomain.com/.well-known/acme-challenge/ABC and making sure it doesn’t 404). If that works, go ahead and press enter on the waiting certbot terminal.</p><p><strong>For DNS:</strong></p><p>It will ask you to add a TXT record to _acme-challenge.yourdomain.com or _acme-challenge.yoursubdomain.yourdomain.com with the contents XYZ (Note: XYZ is a placeholder, not a real value). Go to your domain name provider and add the record, for namecheap it was</p><p>type: TXT<br>host: _acme-challenge (or _acme-challenge.yoursubdomain)<br>value: XYZ</p><p>Then save it and wait for it to propagate. You’ll be able to tell when it’s done because the value XYZ will show up in the output of the DIG terminal command:</p><pre>$ dig -t txt _acme-challenge.yourdomain.com</pre><pre>OR </pre><pre>$ dig -t txt _acme-challenge.yoursubdomain.yourdomain.com</pre><p>Once it appears press enter in the certbot terminal.</p><p>4. Congrats! Certbot has just congratulated you on the fact that you have a shiny new certificate waiting for you at: /etc/letsencrypt/live/yourdomain.com/fullchain.pem</p><p>Head over to Google’s App Engines SSL Certificates Pages (App Engine &gt; Settings &gt; SSL Certificates, or just <a href="https://console.cloud.google.com/appengine/settings/certificates">https://console.cloud.google.com/appengine/settings/certificates</a>), where you’ll be prompted to copy and paste a certificate and private key (note: you can also upload a file, but this is harder as only root has read permissions on /etc/letsencrypt/live/ and Chrome doesn’t have root permissions). The certificate can be uploaded as is, you can use the following command to copy it to your clipboard:</p><pre>$ sudo cat /etc/letsencrypt/live/yourdomain.com/fullchain.pem | pbcopy</pre><p>Before you can upload the private key though, you must convert it to an RSA private key (for the difference see here: <a href="https://stackoverflow.com/a/20065522/781199">https://stackoverflow.com/a/20065522/781199</a>). To do this, run the following command:</p><pre>$ sudo openssl rsa -inform pem -in /etc/letsencrypt/live/yourdomain.com/privkey.pem -outform pem &gt; /etc/letsencrypt/live/yourdomain.com/rsaprivatekey.pem</pre><p>(Note: if you want to write out to a file like that, you’ll need root permissions as only root has write permissions on /etc/letsencrypt/live/, to do that you can use “$ sudo su root”).</p><p>And then copy and paste it into the private key box on Google App Engine:</p><pre>$ sudo cat /etc/letsencrypt/live/yourdomain.com/rsaprivatekey.pem | pbcopy</pre><p>Done! Your site is now HTTPS protected! Don’t forget to renew your cert in 3 months 🔐</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7cfe0928768e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/lets-encrypt-and-google-app-engine-in-2017-7cfe0928768e">Let’s Encrypt and Google App Engine in 2017</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[When Smaller’s Better]]></title>
            <link>https://medium.com/hackernoon/when-smallers-better-4b54cedc3402?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/4b54cedc3402</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[research]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tensorflow]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Mon, 02 Jan 2017 16:42:01 GMT</pubDate>
            <atom:updated>2019-05-20T09:23:57.934Z</atom:updated>
            <content:encoded><![CDATA[<h4>Lossy Compression of Neural Networks in TensorFlow Graph Files</h4><p><em>(This post motivates and describes applications of findings from </em><a href="https://arxiv.org/abs/1510.00149"><em>Han, Mao, and Dally’s paper on neural net compression</em></a><em> on TensorFlow Graph Files. Huge shout out to </em><a href="https://www.linkedin.com/in/mshumikhin"><em>Michael Shumikhin</em></a><em>, </em><a href="http://www.anishathalye.com/"><em>Anish Athalye</em></a><em>, and </em><a href="https://github.com/iahs"><em>Shai Szulanski</em></a><em> — who helped motivate and advance this work.)</em></p><p><strong>TL;DR </strong>Neural nets are big. Sometimes it’s useful when they’re smaller (so you can store more, or deploy them with less bandwidth). <a href="https://arxiv.org/abs/1510.00149">This paper</a> sets forth ways to do theoretical compression of NNs. You can use off the shelf compression algorithms and <a href="https://github.com/tomasreimers/tensorflow-graph-compression">our library</a> to achieve 3x size reductions in TF graphfiles, without having to retrain the graph and with no (noticed) significant loss in accuracy.</p><p>Artificial neural networks have been gaining popularity over the past few years…</p><p>… is an understatement. Machine <a href="https://hackernoon.com/tagged/learning">learning</a> is possibly <em>the</em> most hyped field in computer science right now, and neural nets are used for almost all things ‘machine learning’: sentiment analysis, decision making, image classification, etc. Neural networks work by taking in a ‘training’ set of data (input and output pairs), and learning to predict the output based on the input; once trained, a neural network can predict the outputs for a ‘test’ set (data that it hasn’t previously seen). One example of an input and output pair might be the pixels of an image, and the output could be a value 0 or 1 indicating whether that image contains a dog. Another example might be an input of pixels from an image and the output is a 3-dimensional vector, where the first dimension is 0 or 1 to indicate the presence of a dog, the second dimension indicates the presence of a cat, and the third dimension indicates the presence of a bird. Neural networks in general have been shown to be be incredibly robust and are currently deployed in production at many top companies including <a href="https://hackernoon.com/tagged/google">Google</a>/Facebook/Amazon/Etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/791/0*dOLD0OnEG55x0p7g." /><figcaption><em>Canonical image of a neural net. Figure from </em><a href="http://cs231n.github.io/neural-networks-1/"><em>http://cs231n.github.io/neural-networks-1/</em></a></figcaption></figure><p>The essence of neural networks is a sequence of weighted linear regressions, combined with transformations on the outputs of regressions (these transformations range from the identity function, to tanh, to <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)">ReLUs</a>). The neural network can ‘learn’ these weights through an algorithm called back propagation, which relies on gradient descent to constantly tweak the weights to improve accuracy over the training set of data.</p><p><em>Note: This is an oversimplification, and </em><a href="https://en.wikipedia.org/wiki/Convolutional_neural_network"><em>CNNs</em></a><em> and </em><a href="https://en.wikipedia.org/wiki/Recurrent_neural_network"><em>RNNs</em></a><em> can complicate this — but it will work for demonstrative purposes. If you’re interested in learning more more, I highly recommend reading the intro from </em><a href="http://cs231n.github.io/neural-networks-1/"><em>Stanford’s CS231n</em></a><em>.</em></p><p>Remembering back to 8th grade algebra, linear regressions take in a value, and produce a value by a coefficient and adding a bias. This is the classic y=mx+b equation. Where m is the weight, and b is the bias. In multiple dimensions, this works much the same way, the only difference is that x and m are vectors.</p><p>In a neural network, where one is doing many, many high dimensional regressions —the values for weights and biases can take up a lot of memory. Using <a href="https://www.tensorflow.org/">TensorFlow</a>, a popular machine learning library open-sourced by Google, as an example: a trained deep mnist graph (which classifies <a href="http://yann.lecun.com/exdb/mnist/">handwritten digits</a>, and is used as the <a href="https://www.tensorflow.org/versions/r0.10/tutorials/mnist/pros/">getting started demo for TensorFlow</a>) takes up <strong>12MiB</strong>. The <a href="https://arxiv.org/abs/1512.00567">Google Inception V3 Graph</a> (used for <a href="https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/label_image">image classification</a>) takes up <strong>91MiB </strong>(<a href="https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip">found here</a>, WARNING: large download). As we continue to iterate on these nets and store multiple versions, the size becomes problematic. It would be nice to have a smaller representation to store multiple versions, send over the network to deploy on servers (or self driving cars…), and store on mobile devices.</p><h4>Crash Course in TensorFlow Graphs</h4><p>TensorFlow structures computation in graphs. These graphs store operations (ops) and the relationship between the inputs of an operation and outputs from previous operations.</p><p>These relationships are stored as ‘tensors’, which are basically n-dimensional arrays whose values are not necessarily known and whose shape may be partially known. You can run a TensorFlow graph by feeding values into the graph for some Tensors and running them through operations until you’ve reached the desired output. For example, if I want to model the equation y=mx+b in TensorFlow, I can do it with the following Python:</p><pre>import tensorflow as tf</pre><pre>y = tf.add(<br>    tf.mul(<br>        tf.Variable(2, name=&quot;m&quot;),<br>        tf.placeholder(tf.int32, shape=1, name=&quot;x&quot;)<br>    ),<br>    tf.Variable(1, name=&quot;b&quot;)<br>)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/246/1*vK97pFAU4H3KSGFWQPvIhw.png" /><figcaption>The example TensorFlow graph for y=mx+b.</figcaption></figure><p>As you can see the ops are: “Add”, “Mul”, “m”, “b”, and “x”. Tensors are named by OPERATION:OUTPUT_INDEX, so for example Mul has one output (the result of m*x), and it would be named Mul:0. Variables and placeholders (placeholders are Variables whose value must be fed on any graph run) are also operations with one output tensor (their own value). In this example, the value of m:0, x:0, b:0, are fairly self evident, the Mul operation takes in m:0 and x:0 as inputs to produce Mul:0, and Add takes in Mul:0 and b:0 to produce Add:0. You may notice there is no y in this graph, that is because y is effectively Add:0, although if we really wanted to you could also create a y variable and assign it to Add:0 (although that seems like overkill). To run this graph, one might feed in a value for x:0 and ask for Add:0, using a command like sess.run([&quot;Add:0&quot;], feed_dict={&quot;x:0&quot;: (5,)}).</p><p>These graphs are encoded and stored in a <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/graph.proto">GraphDef</a> protobuf. Values for variables are not stored in a graph def (as they can be assigned over multiple executions) and are therefore not part of the graph; however, variables can be saved in a checkpoint file (which is basically a dictionary containing variable name and value… at least according to Google, the file type is much more opaque than GraphDefs). Variables are useful when we’re fitting a regression (or training neural net), but it doesn’t make much sense to keep them as variables once we know we will no longer be changing them. Instead we can convert them to constant values (either using the <a href="https://github.com/tensorflow/tensorflow/blob/64edd34ce69b4a8033af5d217cb8894105297d8a/tensorflow/python/framework/graph_util_impl.py#L178">convert_variables_to_constants</a> function or the <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/freeze_graph.py">freeze_graph</a> tool distributed with TensorFlow) and simplify our graph. An example GraphDef after freezing our toy mx+b model contains a collection of nodes and (possibly) the value of the tensor they contain:</p><pre>node {<br>  name: &quot;m&quot;<br>  op: &quot;Const&quot;<br>  attr {<br>    key: &quot;dtype&quot;<br>    value {<br>      type: DT_INT32<br>    }<br>  }<br>  attr {<br>    key: &quot;value&quot;<br>    value {<br>      tensor {<br>        dtype: DT_INT32<br>        tensor_shape {<br>        }<br>        int_val: 2<br>      }<br>    }<br>  }<br>}</pre><pre>...</pre><pre>node {<br>  name: &quot;Add&quot;<br>  op: &quot;Add&quot;<br>  input: &quot;Mul&quot;<br>  input: &quot;b/read&quot;<br>  attr {<br>    key: &quot;T&quot;<br>    value {<br>      type: DT_INT32<br>    }<br>  }<br>}</pre><p><em>Note: Tensors with values that have more than one scalar are stored as </em><a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/tensor.proto#L36"><em>packed bytes in a string</em></a><em>. They can be transformed to/from arrays with the </em><a href="https://www.tensorflow.org/api_docs/python/contrib.util/"><em>tf.contrib.util.make_ndarray and .make_tensor_proto functions</em></a><em>.</em></p><p>To read more on the variety of TensorFlow model files, <a href="https://www.tensorflow.org/versions/master/how_tos/tool_developers/">see here</a>.</p><h4>Compression</h4><p><a href="https://github.com/tomasreimers/tensorflow-graph-compression">https://github.com/tomasreimers/tensorflow-graph-compression</a></p><p>Wanting to compress TensorFlow Graph files, the first thing we did was try gzip, a popular compression tool. Gzip was not super effective. It did not significantly reduce the file size of the saved deep mnist graph (rounds to <strong>12MiB</strong> before and after compression)<strong> </strong>and reduced the file size of the inception graph from <strong>91MiB</strong> to <strong>85MiB </strong>(even using gzip -9 compression). Facing similar results with other lossless compression algorithms, I decided to consider lossy neural net compression.</p><p>TensorFlow graph files are largely weights. Stripping the tensors containing the various weights from the GraphDef files, the deep mnist graph is only <strong>3.7KiB</strong> and the inception graph is only <strong>120KiB</strong>. Knowing that, I began to look for ways to lossy compress the weights.</p><p>Neural net weight imprecision has started to emerge as an area of research for a variety of reasons: hardware manufacturers are interested in it because it might allow them to make more <a href="https://www.technologyreview.com/s/601263/why-a-chip-thats-bad-at-math-can-help-computers-tackle-harder-problems/">imprecise hardware that can run neural nets faster</a> (<a href="https://developer.nvidia.com/tensorrt">NVidia’s TensorRT has hardware level support for INT8 and Float16 modes to run nets faster</a>), and software engineers are interested in it because it might allow them to <a href="https://arxiv.org/abs/1602.07360">reduce the space taken up by neural nets</a> and possibly even <a href="http://www.jmlr.org/proceedings/papers/v37/gupta15.pdf">software level speedups or energy efficiency</a>.</p><p>The seminal research in the field of weight compression was conducted by <a href="https://arxiv.org/abs/1510.00149">Han, Mao, and Dally and showed that neural networks could be reduced in size with pruning, quantization, and Huffman coding</a>. This research assumed that one still had access to the training set of the neural net; however, for our purposes we’re going to assume that we don’t have access to that dataset (for example, if we have a pre-trained net from a model zoo). This means that we can not do pruning (which built on <a href="https://arxiv.org/abs/1506.02626">Han’s previous work</a>), as that would require being able to train the network; however we still can do quantization.</p><p>The insight behind quantization is that weights in a neural network are fundamentally imprecise by the very nature of the back-propagation algorithm, meaning that they don’t necessarily need all the precision we allocate for them. Currently, weights (assuming they’re standard 32b floats) can take on any one of 2³² different values, meaning that their representation must have a minimum of 32 bits. However, if we only let the weights take on 2⁸ values (8 is arbitrary, we can pick any integer &lt; 32), we would only need 8 bits to represent them (you could imagine replacing each weight with an integer 0–255, and having a codebook of negligible size to translate the integer to a floating point value for that weight).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*GaEsC2D-U-udWunsaKGA4g.png" /><figcaption>The dynamic programming solution to find optimal clustering in 1D.</figcaption></figure><p>So now the problem becomes, how can we select 2⁸ values to appropriately represent all of our weights? Well this becomes a clustering problem, and Hans et al. did this using K-Means clustering (which finds a local optima) on the collection of scalar values from all of the weights, and replacing each scalar with its cluster centroid. We implemented this algorithm, and realized we could actually improve it (shout out to Shai), because the collection of the scalars in all the weights exists in one-dimensional space, and we can use dynamic programming to find the global optima for clustering in 1D in O(kN²) time and O(kN) space, where k is the number of clusters and N is the number of points being clustered. We implemented optimal clustering <a href="https://github.com/tomasreimers/tensorflow-graph-compression/blob/master/optimal_cluster.py">here</a>, although it will likely need to be reimplemented in C++ if we want it to be fast enough to use on real nets.</p><p><em>Note: We have been minimizing the distance between each scalar and the value we replace it with, using a predetermined number of clusters; however if you have more knowledge of your net and want to use another loss function, you should check out </em><a href="https://arxiv.org/pdf/math/0309285.pdf"><em>this paper</em></a><em> which proposes an O(N²) for clustering without a predetermined number of clusters.</em></p><p>Having replaced all the weight scalars with the centroid of their cluster, we should have the exact same file size because the cluster centroids still have the same amount of bits as the values they replaced. In order to see a size reduction we would need to implement a codebook and replace each weight with a lower bit representation indexing into the codebook. If we replace 32 bit weights with a 8 bit index into the codebook, this should achieve a <strong>4x reduction</strong>. In addition, Han et al. point out that we could use <a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman coding</a> to achieve even greater compression ratios (assuming the weights are not evenly distributed).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*oGeD92fOEJS7vDY4mXVH_w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iUXJpmGMaopvxRH8aGiEvw.png" /><figcaption>Evidence that weights are not evenly distributed in nets, and that Huffman encoding may be useful. The images above depict 98% of the data, I’ve removed the 1st and 99th percentile as they are outliers and extend the range by so much it renders the graphs difficult to read.</figcaption></figure><p>However, implementing a codebook with Huffman coding is painful (or at least time-consuming) and there is no reason we have to do it ourselves (Shout out to Michael for pointing this out): by replacing the weights with their centroids, we’ve increased redundancy in the file; this means that we can use off the shelf compression algorithms and they will create a codebook with Huffman encoding for us (but possibly less with a worse compression ratio than if we did the codebook manually).</p><p>Using GZip on the new files, we compressed mnist from <strong>12MiB to 5MiB </strong>(using a global codebook) and inception from <strong>91MiB to 37MiB </strong>(using a per layer codebook). We also found <a href="http://facebook.github.io/zstd/">Facebook’s zstd</a> to be exceptional and further reduce mnist to <strong>4MiB</strong> and inception to <strong>34MiB;</strong> while these are less than the 4x we should see in theory, it is still a higher compression ratio than we originally had and we’re not dependent on a custom encoding.</p><p>The nets with new weights did not show significant increases in error. Mnist with weight replacement achieved an accuracy of <strong>0.9644</strong>(compared to <strong>0.9642</strong> before weight replacement… the improvement in accuracy is likely due to random chance). An interesting area of further research would be to try and encode the weights with even fewer bits (imagine a 2⁶ entry codebook, where each weight can be encoded in 6 bits) and graph the size/accuracy tradeoff.</p><p><em>I’ve open sourced the weight compression script, and it is available </em><a href="https://github.com/tomasreimers/tensorflow-graph-compression"><em>here</em></a><em>. Improvements and pull requests welcome :)</em></p><h4>Conclusions</h4><p>Neural nets are incredibly powerful and being deployed in more and more places. Currently, the representation of neural nets is space intensive, and this might be a problem as we begin to export neural nets to more and more platforms (both mobile and web, consider projects like <a href="https://transcranial.github.io/keras-js/">Keras.js</a> which is incredibly exciting and pushing the frontiers of what’s possible, but whose <a href="https://transcranial.github.io/keras-js/">demos</a> requires the user to download ~100MiB of weights to run inception in the browser). We demonstrate that we can cluster weights to encode them to reduce file sizes. Additionally, we show there is no need to implement custom decoders because by simply replacing the weights with some similar value we can increase redundancy in the representation of the net and thus increase the compression ratios we can expect with off-the-shelf compression algorithms (<em>added win: gzip is implemented in most servers and browsers by default ;)</em>); all without too much loss of accuracy.</p><p><em>P.s. Throughout this project, Apple Finder’s notion of </em><a href="https://en.wikipedia.org/wiki/Megabyte"><em>base-10 MB</em></a><em> vs Terminal’s notion of </em><a href="https://en.wikipedia.org/wiki/Mebibyte"><em>base-2 MiB </em></a><em>could be wildly confusing at times -_- </em><a href="https://xkcd.com/394/"><em>Relevant XKCD</em></a><em>.</em></p><p><strong>Update: </strong>You can now also do something similar using TensorFlow’s <a href="https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms#quantize_weights">quantize_weights graph transform</a>.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fupscri.be%2Fdde502%3Fas_embed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fupscri.be%2Fhackernoon%2F&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=upscri" width="800" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c851dac986ab6dbb2d1aaa91205a8eb/href">https://medium.com/media/3c851dac986ab6dbb2d1aaa91205a8eb/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b54cedc3402" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/when-smallers-better-4b54cedc3402">When Smaller’s Better</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[10 Questions for Student Startups]]></title>
            <link>https://medium.com/rough-draft-ventures/10-questions-for-student-startups-a59bf76e9138?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/a59bf76e9138</guid>
            <category><![CDATA[perspective]]></category>
            <category><![CDATA[for-students-by-students]]></category>
            <category><![CDATA[students]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Tue, 29 Nov 2016 18:09:21 GMT</pubDate>
            <atom:updated>2016-12-16T19:45:52.151Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Note: This post was co-authored by </em><a href="https://twitter.com/josephwandile"><em>Joe Kahn</em></a><em> (Medium won’t let us add co-authors), definitely check out his profile!</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zvHnrKvUiSHOA7v1mCel9Q.png" /></figure><p>Hi! We’re Tomas &amp; Joe: student partners from two student-run venture capital firms, <a href="http://www.roughdraft.vc/">Rough Draft Ventures</a> (RDV) and <a href="http://dormroomfund.com/">Dorm Room Fund</a> (DRF). We spend a lot of time meeting with founders, learning about different markets, and listening to dozens of pitches.</p><p>After collectively meeting with hundreds of companies, we’ve found that there’s a pretty small set of simple questions we need ask to structure our thoughts. We wanted to post some of these questions online, with hopes that they might be helpful to current and aspiring founders.</p><ol><li><strong>Who are you?</strong></li></ol><p>This should be no surprise. When we are looking at early stage student-run companies, typically the most important thing to us is the team, since it’s often hard to assess the market and product potential at such an early stage.</p><p><strong>2. What is the pain point you’re addressing?</strong></p><p>You’ve probably heard some version of Maslow’s law of the instrument before: “If you have a hammer, everything looks like a nail.”</p><p>Reinterpreted for startups: Don’t fit a product to a problem. Lead with a real problem faced by real people and show us how your company arises from that. Can you quantify how bad the problem is? How big is the market for your solution?</p><p><strong>3. What are you building?</strong></p><p>Having covered team and pain point, we’re going to want to hear about the solution. Having a concise description is important, and being able to explain what that will be now, and a little while into the future is also helpful.</p><p><strong>4. Is this problem best solved as a company? And is this a feature or a product?</strong></p><p>A friend of ours once opined that “doing a company should be the last option you pursue when solving a problem.” If the problem can be solved by shifting a habit or writing up a good tutorial online, do that instead; even if it can be better solved by writing a plugin to a popular piece of software, do that instead.</p><p>If, and only if, the problem will be best solved by creating a company to develop and sustain a solution, that is when you should pursue that.</p><p><strong>5. What have you achieved in the last 3 months?</strong></p><p>We want to hear about how hard you’re hustling. If you come to us and say you have a great idea, don’t really need capital, but aren’t going to start until you have it … that shows us a wild lack of conviction. Prove to us that you’re driven by this problem and that you will solve it with or without us.</p><p><strong>6. Why are you the one to build it?</strong></p><p>Building a company is hard. With few exceptions, you probably are not the first to either consider this space or build a company in it. Why do you believe you’ll be able to?</p><p>This is especially important for college students wanting to build companies in spaces most college students know little about it. If you’re trying to build a product for advertisers, why are you qualified? With that said, there are many examples of college students disrupting fields they had no formal experience in, but explain to us why you’re passionate or what is your unique insight or experience.</p><p><strong>7. Can you build it yourself?</strong></p><p>If you’re building a technical product, one of your cofounders better be technical. If you’re building a company that uses “machine learning”, we’re going to ask what algorithms you’re using, who your ML person is, how long they’ve been doing ML, and so on…</p><p><strong>8. If you have a cofounder, how is your relationship?</strong></p><p>So many great company ideas go up in flames because the co-founders started fighting when running the company became difficult (i.e. user acquisition struggles, hiring struggles, difference in leadership styles, difference in visions, etc). How long have you two known each other? How’d you meet? And have you successfully worked on any projects before? Most importantly, are you actively working on the health of your relationship? What steps have you taken to build a sustainable and trusting partnership?</p><p>Some quality reading: <a href="http://venturehacks.com/articles/pick-cofounder">How to pick a co-founder</a>.</p><p><strong>9. What’s your go-to-market?</strong></p><p>Getting someone to initially trust your company is hard. How are you going to get your first few users? This is orders of magnitude harder if you’re building a two sided marketplace. Think about how you can start with individuals or small groups of users and get usage to grow from there (and no, “all their friends will also join the service at the same time” is not a good answer).</p><p><strong>10. Why is it defensible?</strong></p><p>Assuming your company goes well, and you start to scale and gather users, and are making 100s of billions a year, what’s to stop a large competitor from rolling out the same product? How will you build defensibility in that time so that someone else can’t straight up copy you?<strong> </strong>Does your product benefit from network effects? Is there a regulatory moat?</p><p>If you are a student team: both <a href="http://drf.vc/">DRF</a> &amp; <a href="http://roughdraft.vc/">RDV</a> are made up of students and invest around $20,000 in student-led startups. We’d love to chat and talk about your company more.</p><p>P.S. The <a href="https://startuplaunchlist.com/">startup launch list</a> by Panda is a great resource of blog posts and articles written by thought leaders in technology and venture capital.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a59bf76e9138" width="1" height="1" alt=""><hr><p><a href="https://medium.com/rough-draft-ventures/10-questions-for-student-startups-a59bf76e9138">10 Questions for Student Startups</a> was originally published in <a href="https://medium.com/rough-draft-ventures">Rough Draft Ventures</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Wolfe: Supercharge your Laptop]]></title>
            <link>https://medium.com/rough-draft-ventures/the-wolfe-supercharge-your-laptop-c84e69d03c13?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/c84e69d03c13</guid>
            <category><![CDATA[portfolio]]></category>
            <category><![CDATA[venture-capital]]></category>
            <category><![CDATA[kickstarter]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[gaming]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Mon, 25 Jul 2016 03:31:42 GMT</pubDate>
            <atom:updated>2016-10-25T16:14:37.979Z</atom:updated>
            <content:encoded><![CDATA[<h4>The Wolfe brings desktop-quality graphics to your desktop.</h4><p>Rough Draft Ventures is excited to announce our support of Wolfepack in their mission to supercharge personal computers through the development of external graphics solutions and supporting software.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F1Ytvc7AKEGrbwgVfziUPw.jpeg" /></figure><p>Graphics cards are specialized pieces of hardware in almost every computer that perform computation related to the processing and rendering of images, videos, and animations. Their performance has huge implications on gaming, video/photo editing applications, and machine-learning processes. Despite their importance, many laptops (most notably, many MacBooks) lack powerful graphics cards because they’re large and drain the battery relatively quickly. That means graphic designers, serious gamers, programmers (basically, anyone who nerds out on computers) currently sacrifice quality graphics unless they’re on a desktop.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w9BVITzdTAj-fM_3alkSxg.jpeg" /><figcaption>A prototype of the Wolfe</figcaption></figure><p>Wolfepack provides a level-up for consumers by giving their graphics a significant boost with the Wolfe and the Wolfe Pro. These external devices attach through thunderbolt and increase the compute power of your machine. The Wolfe gives users the portability of a laptop with the power of a high end desktop opening up new worlds for gaming and photo/video editing.</p><p>Early prototypes of the Wolfe have seen frame rate increases from 14fps (on the newest 15-inch MacBook) to 52.4fps (using the Wolfe) and 75.8fps (using the Wolfe Pro, all measured using the <a href="https://unigine.com/products/benchmarks/heaven/">Heaven Benchmarking Suite</a>).</p><p>The Wolfepack consists of three phenomenal Harvard founders: <a href="https://www.linkedin.com/in/dino-rodriguez-249b97124">Dino</a>, <a href="https://www.linkedin.com/in/christianondaatje">Christian</a>, and <a href="https://www.linkedin.com/in/chase-davis-b66a19bb">Chase</a>. All of them are passionate for building computers and gaming. The pack began working on the Wolfe in summer 2015 and has been working full time this summer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Czzm-lDC7cuwoLSKQRaDJA.jpeg" /><figcaption>The Wolfepack (Left to right: Chase, Dino, Christian)</figcaption></figure><p>We’re super excited to welcome them to the Rough Draft family! The Wolfe will be launching on Kickstarter in the next two weeks — <a href="https://www.thewolfe.io/">signup on their website to stay updated</a>!</p><p>Visit <a href="https://www.thewolfe.io/landing">theWolfe.io</a> to learn more and follow them on <a href="https://www.facebook.com/TheWolfe.io">Facebook</a> to keep up to date. Learn more about Rough Draft <a href="http://www.roughdraft.vc/">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c84e69d03c13" width="1" height="1" alt=""><hr><p><a href="https://medium.com/rough-draft-ventures/the-wolfe-supercharge-your-laptop-c84e69d03c13">The Wolfe: Supercharge your Laptop</a> was originally published in <a href="https://medium.com/rough-draft-ventures">Rough Draft Ventures</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pavlov: Image and Video Understanding]]></title>
            <link>https://medium.com/rough-draft-ventures/pavlov-image-and-video-understanding-bee40dfe9b12?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/bee40dfe9b12</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[portfolio]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Thu, 05 May 2016 14:20:12 GMT</pubDate>
            <atom:updated>2016-10-25T14:50:44.879Z</atom:updated>
            <content:encoded><![CDATA[<h4>Pavlov is an artificial intelligence startup structuring image and video data.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EMjikkFnc8ghpalThkHLSg.jpeg" /><figcaption>Alex Kern and Nikhil Srinivasan, cofounders at Pavlov</figcaption></figure><p>Rough Draft Ventures is excited to support <a href="http://twitter.com/_nvs">Nikhil Srinivasan</a> and <a href="http://twitter.com/kerncancode">Alex Kern</a> as they build <a href="http://www.pavlovml.com/">Pavlov</a>, an artificial intelligence company using deep learning to help structure image and video data.</p><p>Machine learning is a field of study that gives computers the ability to learn and recognize patterns that are complex and hard to describe. While the technology is incredibly useful when making sense of large and unstructured data, the tooling that currently exists leaves a lot to be desired.</p><p>Pavlov is focused on training computers to understand what it sees in image and video data, helping businesses make sense of their rich media. Their first product, Pavlov Spot, helps companies detect copyright and trademark at scale via a simple API and web interface. Over the coming months, the company plans to help enterprise customers with drone, medical, and creative content gather and query insights.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7foTdfxm5GUi8H_NbfS6Pg.png" /></figure><p>The cofounders bring incredible domain expertise to the team, having previously built distributed data infrastructure at Apple, NASA, and Harvard Medical School. The company went through the inaugural YC Fellowship batch and is heads down taking their product to market. For a glimpse of what they’re up to check out <a href="https://github.com/pavlovml">GitHub</a>.</p><p>We’re excited to welcome Pavlov to the Rough Draft family and support their their journey ahead!</p><p>Visit <a href="http://www.pavlovml.com/">Pavlov</a> to learn more and follow them on <a href="https://twitter.com/pavlovml">twitter</a> to keep up to date. Learn more about Rough Draft <a href="http://www.roughdraft.vc/">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bee40dfe9b12" width="1" height="1" alt=""><hr><p><a href="https://medium.com/rough-draft-ventures/pavlov-image-and-video-understanding-bee40dfe9b12">Pavlov: Image and Video Understanding</a> was originally published in <a href="https://medium.com/rough-draft-ventures">Rough Draft Ventures</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Thoughts and Drafting]]></title>
            <link>https://medium.com/@tomasreimers/thoughts-and-drafting-4983ff7572fb?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/4983ff7572fb</guid>
            <category><![CDATA[medium]]></category>
            <category><![CDATA[writing]]></category>
            <category><![CDATA[thoughts]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Fri, 26 Feb 2016 21:26:55 GMT</pubDate>
            <atom:updated>2016-02-26T21:26:55.406Z</atom:updated>
            <content:encoded><![CDATA[<p>In 9th grade I used to talk. A lot.</p><p>My photography teacher, who I was very close with — and who is largely responsible for my interest in photography — used to joke that I talked too much. He told me that he wanted me to consolidate my thoughts, and so he assigned me the homework of writing journal entries on various topics: famous photographs, artistic styles, and techniques. The assignments weren’t graded on anything other than completion. He just wanted me to go through the exercise of writing down my thoughts.</p><p>To my surprise, I loved that assignment. The journal entries themselves were little more than my transcribed thoughts and the writing was awful; however, the process was helpful. It forced me to think through otherwise nebulous ideas. Articulating something — either through speech or text — forces you to consider the details. It’s easy when thoughts exist only in thought-space to deceive yourself into believing that you understand something or that your idea is fully formed.</p><p>My photography teacher loved the assignment because it meant that I talked less in class. I loved the assignment because I found that it helped me form my thoughts. Also, once they were on paper, it was easy to revisit and rework them.</p><p>Recently, a close friend and mentor gave me similar advice. I was describing a recent idea for a startup, and he told me to write a blog post about it. I told him I didn’t particularly want to share the idea with the world, and he responded that wasn’t the purpose of writing a blog post. It was to consolidate my thoughts.</p><blockquote>The act of writing your thoughts down consolidates them, gives you a way to share them, and a medium to manipulate them in.</blockquote><p>I think ‘blogging’ (and when I say that I don’t necessarily mean publishing — Medium and many other blog platforms have the ability to let you share drafts without making the draft public) is awesome for a variety of reasons. Two of the most obvious ones are that it forces you to consolidate your thoughts before you share them, and it also gives you a way to share your thoughts with other people. This is why my photography teacher and college mentor both encouraged me to write my thoughts down.</p><p>The other benefit that I think many people overlook is that writing your thoughts down gives you a medium to manipulate them in. In many ways, writing something down makes it tangible. I can edit, rework, and rewrite a paper. No one tries to do complicated math in their head, and the same should apply to other forms of thought: write them down so you can think through and work with specific parts.</p><p>A while ago I got into the habit of writing down my thoughts as ‘blog posts’ on Medium. Only recently have I started to revisit and edit these drafts. I don’t know how many I’ll publish yet, but I thought I would share the process because it’s been valuable to me so far.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4983ff7572fb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[College Startups]]></title>
            <link>https://medium.com/@tomasreimers/college-startups-214717637ef0?source=rss-92483908161b------2</link>
            <guid isPermaLink="false">https://medium.com/p/214717637ef0</guid>
            <category><![CDATA[college]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Tomas Reimers]]></dc:creator>
            <pubDate>Sun, 14 Jun 2015 00:44:28 GMT</pubDate>
            <atom:updated>2015-06-14T00:44:28.037Z</atom:updated>
            <content:encoded><![CDATA[<p>And A Tale of Two Cities</p><p>Let me tell you a story:</p><p>Once upon a time, there were a group of friends in college interested in technology and entrepreneurship. In this city, there were many restaurants, and these restaurants sympathized with the crazy college-student work hours and stayed open late into the night to provide food after the dining halls closed. Unfortunately, these businesses didn’t have the capacity to deliver the barrage of 1am orders, and the students didn’t have an integrated and easy way to order delivery.</p><p>Equipped with coding skills, our main characters believed they could carve a niche in the space. While the market was dominated by companies such as GrubHub and Seamless, they believed that their product could target the local audience in a way that these companies didn’t. They built out their product, and within the first week of launching they got many orders…</p><p>At this point, the story has (at least) two possible endings:</p><p><em>(Ending #1)</em> …within the first week of launching they got many orders, and that number grew, and grew, and grew. The company partnered with more and more restaurants and opened in more and more cities and eventually became a nationally-known company.</p><p><em>(Ending #2)</em> …within the first week of launching they got many orders, and that number grew, and grew, and then the operation was abruptly halted by the university administration, which demanded the students stop business or face disciplinary action.</p><p>Those stories aren’t made up.</p><p>The first ending is the story of <a href="http://blog.doordash.com/post/63110511307/the-doordash-story">DoorDash</a>, a <strong>Stanford-based </strong>startup that <a href="http://www.stanforddaily.com/2013/09/23/alumni-founded-startup-caters-to-palo-alto-mountain-view-food-deliveries/">partnered with local businesses to deliver late-night food to students at Stanford</a>. Eventually expanding to SF, Boston, DC, Chicago, LA, etc. They have <a href="https://www.crunchbase.com/organization/doordash">raised almost $60M in 4 rounds </a>and have been wildly successful as a company.</p><p>The second ending is the story of <a href="http://www.thecrimson.com/article/2014/3/6/instanomz-brings-late-night-food/">Instanomz</a>, a <strong>Harvard-based</strong> startup that <a href="http://blog.harvardventures.org/2014/03/07/akshars-experience-cofounding-instanomz/">partnered with local businesses to deliver late-night food to students at Harvard</a>. Once they gained traction, they were approached by the administration and told that they had to shut down, strike a deal with the HSA (Harvard Student Agencies, a monopoly on businesses that serve Harvard students), or <a href="http://www.thecrimson.com/article/2014/4/17/harvard-instanomz-food-delivery-shut-down/">face disciplinary action</a>.</p><p>Now, let me start by saying that:<strong> I am <em>not </em>presenting this as insult or criticism, simply an observation about differences on campuses.</strong> I agree that these two aren’t identical cases. It is true that Instanomz was breaking a Harvard rule of a entering dorms as a 3rd party delivery service, but I can’t imagine DoorDash didn’t break some obscure rule in the student handbook. Or, if they truly didn’t, it’s possible that those kind of rules are stifling to innovation, and simply shouldn’t exist.</p><p><strong>I <em>am</em> presenting this because I do not believe that all campuses are equally welcoming to innovation. </strong>And I think that when so many administrators are concerned about how to bring innovation to campus, a lot of them simply miss the point.</p><p>In some respects it’s almost paradoxical. On one hand, these are great universities that are doing their part to encourage innovation. At Harvard this means: having <a href="https://i-lab.harvard.edu/">a new building for innovation</a>; it means having <a href="http://harvardi3.org/">a showcase for innovation</a>; it means having <a href="https://i-lab.harvard.edu/experiential-learning/presidents-challenge">an award for innovation in certain sectors</a>.</p><p>On the other hand, in some respects it’s almost <a href="http://www.imdb.com/title/tt1285016/">Social Network</a> esq, that the administration acts as a barrier to innovation rather than a support, forcing students to choose between innovating and facing disciplinary action, or backing down. And that’s not how it should work. When universities are trying to increase innovation, they should encourage it, not hinder it. Encouraging innovation means that they need to be willing to let it change the way the world works.</p><p>It reminds me of a story that gained a lot of publicity about a year ago. A handful of students at Yale built a better way to index course data, called Yale Blue Book Plus (Yale Blue Book is the name of their course evaluations). <a href="http://haufler.org/2014/01/19/i-hope-i-dont-get-kicked-out-of-yale-for-this/">The administration blocked their site and told them that if they didn’t remove it, it would result in disciplinary action.</a></p><p>If universities want more “innovation,” they need to be aware that innovation comes at the cost of changing the way students explore course data; they need to be aware that it causes students to run websites from their dorm room; and they need to be aware that sometimes it leads to disruption of monopolies and late night food delivery. Innovation comes at the cost of potentially disrupting the way we currently do things and changing our perspectives on the world. And so,<em> </em>if universities want to encourage more innovation<em>, they need to focus on first building a culture that is open to growth and trying new things.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=214717637ef0" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>