<?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 Maciek Dziubiński on Medium]]></title>
        <description><![CDATA[Stories by Maciek Dziubiński on Medium]]></description>
        <link>https://medium.com/@ponadto?source=rss-e51fd42b6648------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*aCwKxogBDot5kcwQ.</url>
            <title>Stories by Maciek Dziubiński on Medium</title>
            <link>https://medium.com/@ponadto?source=rss-e51fd42b6648------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 11:46:39 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ponadto/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[Drive faster with machine learning]]></title>
            <link>https://medium.com/asap-report/drive-faster-with-machine-learning-c8d40106a1f4?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8d40106a1f4</guid>
            <category><![CDATA[reinforcement-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[autonomous-cars]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Mon, 03 Jan 2022 14:21:25 GMT</pubDate>
            <atom:updated>2022-01-03T15:45:16.787Z</atom:updated>
            <content:encoded><![CDATA[<p>Racing drivers hate him for this one simple trick</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mIP8DnkslpfQJy9CebTNnQ.png" /></figure><p>There’s this autonomous driving challenge called <a href="https://f1tenth.org/">F1tenth</a> that has both a “physical” version (in which you need a physical car to participate, 1/10th the scale) and a “virtual” one where anyone can compete. In this blog post we’ll focus on the virtual race which comes with a gym-like environment called <a href="https://github.com/f1tenth/f1tenth_gym">f1tenth_gym</a> which looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*ftMel9gl8v2JudKc56KOdw.gif" /></figure><p>Yea, I know — you can almost smell the burning rubber :)<br>But actually, the simplicity of this environment (and most gym environments, I suppose) is its greatest strength. You can prototype quickly and test out more ideas. This is the reason I switched to this environment from the CARLA simulator which, although prettier, is sometimes too cumbersome.</p><p>The controller that’s driving the car in the short clip above is called <a href="https://www.coursera.org/lecture/intro-self-driving-cars/lesson-2-geometric-lateral-control-pure-pursuit-44N7x">Pure Pursuit</a> and the best lap time I got was <strong>52.47s </strong>after testing out more than 1000 different parameter combinations. The topic of this blog post is: how I got that lap time down to <strong>44.35s </strong>using the same data I collected while searching for optimal controller parameters.</p><h3>Introduction</h3><p>The car has two actuators: the <em>steering angle</em>, and the desired <em>speed</em> (and not acceleration, as one might expect from a “normal” car).</p><p>I used a Pure Pursuit controller which essentially aims the steering angle at some point that lies further down the road on a pre-defined path, thus giving a pretty easy but quite robust way of choosing the first actuator. (By the way, if you’d like to know more about the Pure Pursuit controller look no further than <a href="https://www.coursera.org/lecture/intro-self-driving-cars/lesson-2-geometric-lateral-control-pure-pursuit-44N7x">this</a> lecture.) That distance is often called the lookahead distance and I should note that normally this distance depends on the speed of the vehicle, but in my implementation it’s actually frozen for the entirety of the race, just to make it even simpler.</p><p>You still need to come up with the second actuator, speed. The simplest approach: stick to a pre-defined speed unless the centrifugal force exceeds a tire force threshold and if it does, reduce the speed to a value that is right below that point. Obviously, this simplification is bound to fail because: the vehicle’s dynamics are way more sophisticated, and also because although we ask for a particular speed, the car can’t just instantaneously “make it so”. For example: say we’re going 10m/s and our procedure determines that the speed should actually be 5m/s tops. Asking for a particular speed is not the same as magically setting the speed of the vehicle to that value, it takes a while to get there.</p><p>We thus arrive at a pretty simplistic controller whose behavior (and overall performance) depends on three parameters:<br>• lookahead distance | 4.24m<br>• speed setpoint | 10.62m/s<br>• tire force max | 13.42N<br>where the values provided are those for which I got the best lap time, just to give you an idea about their magnitudes.</p><h4>Motivation</h4><p>The problem with this controller is that you might find parameters that give you OK-ish results for one set of conditions (a particular corner, let’s say) but not for the whole race track. In particular, the track in which I was interested in has a lot of straights:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RZgTaePvIrYpzLJygb6rtA.png" /></figure><p>Our simple controller can either drive well through the corners, or drive fast in those straights, not both. One would need to do path optimization to be able to go through the corners faster, or add an “if” statement to the code to drive fast in the straights and slow through the corners, or any other trick to make it more swanky. This would increase the overall number of parameters and, honestly, would feel a bit clunky. But also, I had a bigger picture in mind, namely: I wanted to have a procedure that can be generalized to racing against opponents. (In the next blog post, though.)</p><p>The idea was to train a model that emulates the behavior of our weak controller for a given set of parameters and then use that model to continuously determine the best parameters. This requires two things: 1) a method of scoring the parameters that will allow us to determine which are “best”, and 2) a procedure for determining the best parameters at a given situation without evaluating all possible combinations of parameter values in real time, during the race.</p><h3>Methods</h3><h4>Model</h4><p>The input to the model are three vectors: the state of the vehicle, the path that we’re supposed to follow, and the parameters of the controller. And there are two output vectors: the future trajectory, and the future actuators that are needed for that trajectory to come true.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*17XpKQQGxKzwumx74LInLw.png" /><figcaption>The colorful blocks represent modules that contain one or more fully connected layers.</figcaption></figure><p>INPUTS<strong>:</strong><br>The<strong> state</strong> of the vehicle<strong> </strong>comprises 5 numbers: longitudinal and lateral speeds, angular speed (Z-coordinate), and current actuators (steering angle and speed). Note that there’s no position nor yaw. This is because we’re already operating in a coordinate system that’s centered on the car (its position is always (0, 0)), with the X-axis shooting forward in the direction in which the vehicle is pointing (so yaw is always 0).</p><p>The <strong>cent </strong>stands for <em>centerline </em>which is the path we’re following. As the name suggests, this is the path that lies exactly in the middle between the race track’s bounds. (Clearly, this path is not the fastest but we’ll worry about that more in the next blog post.)</p><p>And finally, the <strong>params</strong> are the controller parameters (lookahead distance, speed setpoint, and tire force max) that were set while the data was recorded. The idea is to look for better params while driving via an online gradient ascent procedure such that we constantly adapt to the changing conditions.<br>_________</p><p>OUTPUTS:<br>The <strong>traj</strong>ectory are 150 2D points representing future positions of the vehicle, and the <strong>act </strong>is a vector containing future actuators: 9 steering angles, and 9 speeds.</p><p>We need the trajectory to compute the score later on, whereas the actuators are, well, needed to drive the vehicle. Although I should say: not all of them, I’m only taking the first steering angle and speed and throw away the rest (as you would normally do in a controller like Model Predictive Control, which I used in a <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">previous blog post</a>). <br>_________</p><p>The colorful boxes in the graph depict modules containing one or more fully connected layers, nothing fancy.</p><p>During inference, the state and centerline are given, we can’t change them. But the idea is to probe the model for alternative controller parameters that might yield a more promising trajectory.</p><h4>Score</h4><p>Initially, I used the term “return” but then I realized it might confuse reinforcement learning savvy readers so I settled for “score”. Remember, we’d like to be able to say how good the current params are (those used as input to the neural network), i.e. we want to assign a score to the controller parameters. I used the following formula to compute the score:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JuUza6DsuzxTqPLLRPtwcQ.png" /></figure><p>where <strong>progress</strong> is a function of the trajectory and the centerline and is simply the total distance travelled along the centerline, whereas the <strong>penalty</strong> is a function of all the distances between the points in the trajectory and all the points in the nearby bounds of the racetrack. See the <a href="https://github.com/MTDzi/racing_utils/blob/master/racing_utils/torch_related.py#L89">code</a> for more details.</p><p>During inference, while driving the vehicle, I’m estimating the gradient of the score with respect to the controller parameters and I’m doing that by feeding the model a batch of size 25 in which all states and centerline inputs are the same, but the controller parameters differ and these are: the current param values + 24 neighboring values needed for estimating the gradient.</p><p>I continuously update the controller parameters as you would normally do in a gradient ascent loop:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0_-iBC2fv8c726ufju2TxQ.png" /></figure><p>This online optimization probably never reaches the optimum, partly because the score landscape constantly changes, and partly because this is a pretty crude approach, but the hope was that it at least adapts quickly enough to new conditions.</p><h3>Results</h3><p>This is the graph representation I used to determine if the model drives as intended (see the full video <a href="https://www.youtube.com/watch?v=1nWquEk89H4&amp;ab_channel=ponadto">here</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HNQYkdI2ZRvDByRJBpRcSQ.png" /></figure><p>The upper row depicts model predictions (going from left): the future trajectory (150 steps into the future), and 9 future steering angles, and 9 future speeds. The red lines and dots represents the best prediction in a batch of size 25, blue represents the results for the current controller parameters. All those grey lines correspond to suboptimal input params.</p><p>The bottom row contains a minimap + values of progress and penalty calculated for the predicted trajectories. Both plots have observation indices sorted according to their progress values so that we can quickly see how big the penalty is for the trajectory that goes the furthest.</p><h4>Data</h4><p>I’d like to be able to test out this approach on an actual car (1/10th scale, no need to panic) so I needed to consider two things: safety and how much data is needed.</p><p>Safety is ensured as long as any walls are far from the path the car is following and the localization algorithm works correctly. If both are true, I can sample controller parameter values from a fairly wide range and run data collection without additional supervision.</p><p>The best lap time I got (44.35s) was when I trained a model on data from 1000 races, each race lasting for 110s simulation time (maybe 7s of real time), so that’s 1000 x 110s = 30.6h, way too long. But I also made experiments where only 32 races were conducted (which is ~1h worth of simulation time) and the best lap time then was 45.49s which is “good enough”. Our real world is a bit more complicated so that 1h of simulation time might translate to just about anything, but at least it gives me some hope it will be attainable.</p><h4>Inference time</h4><p>Inference on my workstation (GTX 1080Ti) along with progress and penalty calculations (which are also done on the GPU) takes ~1.5ms. That’s for a batch of size 25, but I also checked how long it takes for a batch of size 512 and it was ~3.1ms. I don’t know yet how fast it will be on a Jetson but, again, this looks promising, even before any additional speedups (I’m thinking of TensorRT, that’s the one I know the best).</p><h3>Discussion &amp; future work</h3><p>It’s not lost on me that the lap time reduction wouldn’t be so profound if I used a more sophisticated controller. In particular, an MPC that optimizes the distance travelled along the centerline would probably lead to a model wouldn’t have such a significant lap time reduction (unless, for example, there’s something very wrong with the dynamical model used for the controller). Speeding up the Pure Pursuit controller wasn’t really the point here, I just wanted to know if I’m heading in the right direction, if there aren’t any critical errors in my code.</p><p>The fact that the lap time went down is a good sign, and now I can move on to the next step which is the optimization of the “cent” input that’s being used by the model. Currently, that’s not feasible because this feature has a size of 600 (300 2D waypoints), but it can be substantially condensed. Once that’s done, and once I figure out how to effectively sample the space of possible waypoints, I should not only be able to follow a faster path, but I’m also hoping to avoid collisions with obstacles.</p><p>The next step after that is avoiding collisions with opponents. For that I would need to at least roughly forecast their future positions. But wait a second… the current model can do just that, all I need to do is make the batch a bit larger, it will still be blazingly fast. (Assuming I know the state of my opponents, but that’s “doable”.) Then I can calculate the penalty using not only the bounds but also the predicted trajectory of the opponents and optimize the score as I’m doing currently.</p><p>This is where I am right now and where I’d like to get from here. The project is split into three repos:<br>* a <a href="https://github.com/MTDzi/f1tenth_gym">fork</a> of the f1tenth_gym project<br>* <a href="https://github.com/MTDzi/offline_rl">this</a> repo for training the model<br>* and <a href="https://github.com/MTDzi/racing_utils">this</a> overarching repo (a Python package) containing code used in both of the above projects.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*tsDIQS1TxgvAfuIR.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*LaQ7zrNVOzAX28gn.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8d40106a1f4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/drive-faster-with-machine-learning-c8d40106a1f4">Drive faster with machine learning</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Training a neural network for driving an autonomous RC car]]></title>
            <link>https://medium.com/asap-report/training-a-neural-network-for-driving-an-autonomous-rc-car-3906db91f3e?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/3906db91f3e</guid>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[embedded-systems]]></category>
            <category><![CDATA[autonomous-cars]]></category>
            <category><![CDATA[robotics]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Mon, 02 Sep 2019 07:33:37 GMT</pubDate>
            <atom:updated>2019-09-02T07:33:37.949Z</atom:updated>
            <content:encoded><![CDATA[<p>About machine learning and mobile robotics, and how “they go together like a horse and carriage”.</p><p>After several months of building a model of a self-driving car, I was able to get it to drive around a pond that’s next to my house. Here’s a teaser:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*ZZ1k6tKxxzbRjuV0LbfmUA.gif" /><figcaption>A full-color video below</figcaption></figure><h4><strong>The “what”</strong></h4><p>It’ll be easier for me to refer to the mobile robot by it’s name, “Karr” (an homage to <a href="https://en.wikipedia.org/wiki/KARR_(Knight_Rider)">KARR</a>), simply because it’s shorter than “an autonomous RC car” and less ambiguous.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TGflUEqkg8kcfqildX_O5w.jpeg" /></figure><p>Karr was strongly inspired by the specification for the <a href="http://f1tenth.org/build.html">F1/10th competition</a> but it doesn’t have a LIDAR (which is too damn expensive) and instead uses a <a href="https://www.intelrealsense.com/depth-camera-d435i/">depth camera</a> as it’s primary sensor. It’s main computing unit is the <a href="https://developer.nvidia.com/embedded/jetson-tx1">NVIDIA Jetson TX-1</a> and the chassis is a two-wheel drive <a href="https://traxxas.com/products/models/electric/58024slash">Traxxas Slash</a>. The depth camera is mounted on top of a “safety cage” scaffold (built by my Dad — thanks, Dad!) so that: 1) it has a better view point, 2) the hardware as a whole is safer (in case of a rolloever or an unfortunate collision).</p><p>Most notably, the computing unit has a GPGPU that supports CUDA which, in particular, let’s you get a shorter inference time for a neural network than on a CPU of, for example, a Raspberry Pi. With 256 Maxwell CUDA cores it actually allowed for the model to yield predictions in ~3.5ms (pre-processing and everything else included)!</p><p>Here’s how the world looks like from Karr’s perspective (<a href="https://www.youtube.com/watch?v=m7twhw8Vh1I">full movie</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*oNMStuaRShRkCGqNBNnbzA.gif" /></figure><p>The closer something is, the darker it is in the depth map. Also, although this will not be relevant in this article, a depth camera lets you get point clouds which can then be used e.g. for SLAM (Simultaneous Localization and Mapping). This is how it looks like:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FL8Jz4Q3KQNM%3Fstart%3D2%26feature%3Doembed%26start%3D2&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DL8Jz4Q3KQNM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FL8Jz4Q3KQNM%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a2bbc33cc2408960e3fe87b6f712b856/href">https://medium.com/media/a2bbc33cc2408960e3fe87b6f712b856/href</a></iframe><p>This is a mapping of the aforementioned pond that’s next to my house, and the path around it — that’s the “race track” around which I wanted the neural network model to be able to drive.</p><h4>The “why”</h4><p>My ultimate goal is to train a neural network on depth maps in the CARLA simulator so that it can drive Karr in the real world. But that’s for another blog post, in this one Karr was driven by a model trained using the same pipeline I implemented in a <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">previous project</a> (which I tested out in a simulation environment). The only two differences were: 1) the model was simpler, and 2) the data was produced by a richer, more convincing simulator — our reality ;)</p><h3>Software</h3><p>The neural network was trained using keras, however it was <em>much </em>easier to install Theano rather than TensorFlow on the Jetson so I used the former as backend.</p><p>The depth camera has its own dedicated library, <a href="https://github.com/IntelRealSense/librealsense">librealsense,</a> with various plugins (e.g. for Unreal Engine 4), APIs (in particular, for Python), and an impressive suite of additional tools, tutorials, and example use-cases. I compiled this library with -DBUILD_WITH_CUDA flag (<a href="https://www.jetsonhacks.com/2018/04/25/now-cuda-intel-realsense-d400-cameras-nvidia-jetson-tx/">“looky here”</a>) thus yielding 60FPS, although running with the lowest resolution (which was far more than I needed).</p><p>I didn’t mention one more hardware component: an Arduino-like microcontroller, <a href="https://store.arduino.cc/teensy-lc-usb-development-board">Teensy-LC</a>. The Jetson communicates with this microcontroller which then sends a <a href="https://en.wikipedia.org/wiki/Pulse-width_modulation">PWM signal</a> that controls the power of the engine and the steering angle of the front wheels. For that, the microcontroller needed a small program which I wrote using the standard <a href="https://www.arduino.cc/en/main/software">Arduino IDE</a>. The source code can be found <a href="https://github.com/asap-report/traxxas_control/blob/master/arduino/traxxas/traxxas.ino">here</a>.</p><p>Also, I used a wireless <a href="https://www.amazon.com/Logitech-940-000117-Gamepad-F710/dp/B0041RR0TW">game pad</a> for manual control, and no, I didn’t have to program that, but it’s good to mention this now because I wanted to make a general point regarding software: <strong>it was getting complicated</strong><em>.</em> There are all these peripherals, each with its own API, each sending messages asynchronously at its own rate, each prone to its own set of errors... you get the idea. And the aim is to bring all these elements together, collect data for training, deploy the model, use its predictions to control the motor and the steering angle, improve, and repeat. Phew.</p><p>To manage all that, I used a framework called the <em>Robot Operating System</em> (ROS) which isn’t actually an operating system (strictly speaking) but it’s so rich in functionalities, submodules for all sorts of sensors, tools for messaging, for data visualization, and so much more — that by virtue of its utility it deserves the “operating system” title. If you haven’t heard about ROS before, check out the <a href="https://en.wikipedia.org/wiki/Robot_Operating_System">Wikipedia article,</a> it gives a pretty good overview.</p><p>But, roughly, ROS abstracts the various sensors and procedures being calculated as a <em>computational graph</em> in which <em>nodes</em> correspond to processes and edges to <em>topics</em> through which processes get their required data in a form of structured <em>messages</em>. I’ve written those terms in italics because those are terms actually used in ROS (for a reference check out the <a href="http://wiki.ros.org/ROS/Concepts">Concepts ROS wiki page</a>). The messages are passed asynchronously, each at its own rate, with processes being triggered by the arrival of a new portions of data.</p><p>I used ROS’es wrappers for communicating with the <a href="http://wiki.ros.org/realsense2_camera">depth camera,</a> with the <a href="http://wiki.ros.org/joy">game pad,</a> and with the <a href="http://wiki.ros.org/serial">microcontroller</a>. But ROS is not just a repository of wrappers, it’s also (and in my opinion, first and foremost) a development platform. Thus, I used ROS to implement a so-called <em>package </em>for controlling the car, traxxas_control, and if you’re interested, I’ve open-sourced <a href="https://github.com/asap-report/traxxas_control">the code</a>.</p><p>The traxxas_control package is organized into <em>nodes </em>for<em> </em>controlling the car using: a neural network, a game pad, and a heuristic based on the depth map. I gathered data for training by steering the car myself, and by using the heuristic. In the next section I’m discussing how I actually trained the model and on which data.</p><h3>Training the model</h3><p>I trained a neural network model to clone <em>some</em> method of steering the car, human-generated or deterministic. As mentioned above, I used a pipeline developed for a previous project, however I needed to keep the model simple so that the inference on the Jetson was fast, yet complex enough to perform well. The architecture uses a single convolutional layer and predicts steering angle and throttle for 10 steps into the future. The architecture is summarized in the following plot (it’s a bit perplexing, I know, but you can check out <a href="https://github.com/MTDzi/carla/blob/7db5aa46d9ebaf930a55b23638a308c71d6859cd/PythonClient/racetrack/train_on_depth.py#L453">the code</a> for a detailed definition):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VJHZlgCYzA7-_8MxkPkbcA.png" /></figure><p>When driving at low speed (see the movie in the <strong>Results</strong> section), I used the steer__1 prediction, i.e. steering angle 1 step into the future. The reason is that getting an image from the camera, processing it, producing a prediction, and then communicating that to Karr’s actuators — all this takes time, a prediction for that image is overdue. I’ve calculated the latency and it was roughly the same as the rate at which the images come from the camera. But that’s a rough estimation, I’ll need to account for speed of the car in the future. For medium and high speeds I used steer__4 and steer__8 respectively, they seemed to be handling the car better but I haven’t really made a systematic analysis, it’s completely subjective.</p><p>I’m ignoring the model’s throttle predictions for now. It’s not that I don’t trust the model (I don’t), I just wanted to keep the setup simple, focus just on the steering angle.</p><p>Also, going back to inference time, I needed to reshape the depth map images from 424×240 to 42×24. This is how the world looks like after reshaping (I’ve also included model predictions):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*C3DhA53tjmGFNXyMgwfh0Q.gif" /></figure><p>Not great, not terrible. I wouldn’t want to drive in those conditions, but the lower resolution didn’t deteriorate the quality of the model that much — MSE on the test set was ~0.010 and ~0.009 correspondingly for the decimated and normal resolution variants.</p><p>On a different note, I anticipated there will be problems in mimicking the way a human controls the car. In a<a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940"> previous blog post</a> I focused on how different sources of data (human-generated vs. deterministic) influence the quality of the model and concluded that it’s relatively difficult to train a model based on expert data (i.e. generated by a human driver).</p><p>But that was in a simulator — what’s the case if we’re operating in reality?</p><h4>First approach: human-generated data</h4><p>As it turned out, reality aggravates the problem with training a model on expert data. I was able to get the model to ride around the track, but it required multiple corrections and very low speed. I’m not going to show its performance in this blog post, I think I might work on it a bit more. I’m confident I could gather the right type of data (drive away from the track’s barriers and thus “teach” the model how not to get off the track, <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">like I did in a simulation</a>). But I knew it’ll be easier to train a model based on a more consistent method of steering than that coming from a human, and that’s what I focused on.</p><p>The behavioral cloning approach is pretty hard and I’ve already mentioned the reasons “why” in earlier blog post, but suffice it to say that humans base their decision on more than an ML model has at its disposal + they’re rather inconsistent. But I refuse to leave it like that, I’m currently working on this problem and… we’ll see, that’s all I can say for now.</p><h4>Second approach: heuristic based on depth map</h4><p>Instead, I used a simple, deterministic procedure that simply steers in the direction in which there’s the most space to go. It’s a variation of a method called “follow the gap”, with a few differences that account for: 1) the fact that the view is constantly shaking, 2) noise in the depth map, and 3) a pretty narrow field of view of the camera.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*kbQCP67o__jrgKiLP2uQFw.jpeg" /></figure><p>Roughly, the idea is to take a set of points lying on an elliptical arc, take depth readings in the neighborhood of each point, calculate the average depth in that neighborhood, and then choose the point with the highest average depth. That point corresponds to a steering angle. I stored a decimated image and its corresponding angle, and those pairs (image, angle) were then fed to the model. On the left is a depth map in which the points lying on the arc were replaced by their average neighborhood distance.</p><p>Prediction took, on average ~35ms (on the Jetson) using this method. That’s actually not bad and it’s definitely “driveable”. However, a neural network model trained on the labels generated with this heuristic yields a prediction (including communication and pre-processing) in <strong>~3.5ms </strong>even though the GPU has just “only” 256 CUDA cores.</p><p>This is a neat trick: make a costly computation on a larger machine, use the result as label for a neural network, train it, and then use its predictions for making decisions on a smaller, embedded system.</p><h3>Results</h3><p>I’m going to first introduce you to the pond next to my flat around which Karr was driving, then I’ll show three videos of the car driving at: low, medium, and high speed. And then, I’ll include a short video of how the same neural network performed in an entirely different environment.</p><h4>Driving around the pond</h4><p>Here’s how the pond looks like on Google Maps:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/810/1*P_kkSXS60JYQzEFVVfSF-w.png" /></figure><p>And here’s a video from the neural network controlling the steering angle and making a single lap around the pond, low speed:</p><iframe src="https://cdn.embedly.com/widgets/media.html?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DCJFjwQiajLA&amp;src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FCJFjwQiajLA&amp;type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/743c42ede442e60e729679e39e7ed214/href">https://medium.com/media/743c42ede442e60e729679e39e7ed214/href</a></iframe><p>Here’s a lap at medium speed (sorry for the <a href="https://en.wikipedia.org/wiki/Shaky_camera">shaky camera</a>, I had to run behind the car):</p><iframe src="https://cdn.embedly.com/widgets/media.html?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DvduTcu9tU7w&amp;src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FvduTcu9tU7w&amp;type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7b533502194c6c5c88aef9a99d1c87a5/href">https://medium.com/media/7b533502194c6c5c88aef9a99d1c87a5/href</a></iframe><p>And here’s one of the turns at high speed:</p><iframe src="https://cdn.embedly.com/widgets/media.html?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DF-myrMngt08&amp;src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FF-myrMngt08&amp;type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/3a421ab0cbb705ee5865a6a446ebbc75/href">https://medium.com/media/3a421ab0cbb705ee5865a6a446ebbc75/href</a></iframe><p>I didn’t capture the whole lap because I didn’t trust myself to react fast enough to stop the car and avoid any possible collisions, but also: I would look absurdly sprinting around the pond behind a “toy car”.</p><p>As a bonus, and to give you an idea of how fast a car like this can go, here’s a short video at maximum speed, on a straight, but still controlled by the neural network:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FPybFc8SqEXE%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DPybFc8SqEXE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FPybFc8SqEXE%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/61f27aeb3ba6737c2a7ff2046a56e7d4/href">https://medium.com/media/61f27aeb3ba6737c2a7ff2046a56e7d4/href</a></iframe><h4>Driving in a cellar</h4><p>I used the <em>exact</em> same NN model and tested it out in a completely different environment (<a href="https://www.youtube.com/watch?v=nMoaJjRHgBc">full movie</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Z32hSNCyr-ztktZP6-l-gQ.gif" /><figcaption>Two hacks and—et voila, the model can take a U-turn in a completely different environment!</figcaption></figure><p>This is a corridor in my cellar and it’s just one turn, I know, but it’s a 180°, much tighter turn than any of the ones Karr experienced driving around the pond. I had to use two hacks to make it work (can you guess what they were?), but honestly, it was way easier to get to work than I anticipated. And as a result a model trained on data gathered around the pond also works indoors.</p><p>I think that’s awesome :)</p><h4>Acknowledgements</h4><p>I would like to thank:</p><ul><li>the company <a href="https://skriware.com/">Skriware</a> for 3D-printing the blue body parts you could see on top of the Traxxas chassis</li><li>the organizers of the <a href="http://www.f1tenth.org/">F1/10th</a> competition for providing excellent materials for amateurs like me to follow and build my own autonomous RC car. The upcoming F1/10th competition will take place in New York, in October (we’ll be there!), and the one after that will be in Berlin, probably in April or May 2020. It’s an awesome event, with great atmosphere and people, and you, Dear Reader, should definitely take part in it ;)</li><li>and last but not least— my Dad for building the scaffold to which the camera was attached.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*qNhhylbLH-PLnxIrbk1rWg.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3906db91f3e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/training-a-neural-network-for-driving-an-autonomous-rc-car-3906db91f3e">Training a neural network for driving an autonomous RC car</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From semantic segmentation to semantic bird’s-eye view in the CARLA simulator]]></title>
            <link>https://medium.com/asap-report/from-semantic-segmentation-to-semantic-birds-eye-view-in-the-carla-simulator-1e636741af3f?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/1e636741af3f</guid>
            <category><![CDATA[robotics]]></category>
            <category><![CDATA[self-driving-cars]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[computer-vision]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Mon, 13 May 2019 16:33:19 GMT</pubDate>
            <atom:updated>2019-05-13T16:33:19.500Z</atom:updated>
            <content:encoded><![CDATA[<p>Building a neural network for reconstructing the top-down view from four side cameras.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_VUKbQbZ23WGVugri2PZqA.jpeg" /><figcaption><em>Modo de volar (A way of flying) by Francisco Goya (source: </em><a href="https://en.wikipedia.org/wiki/Los_disparates">https://en.wikipedia.org/wiki/Los_disparates</a>)</figcaption></figure><p>In this blog post I’m presenting a neural network architecture for transforming this type of input:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t5y7g-ZaNtRlrXMCzYS65A.png" /><figcaption>Four side cameras: FrontSS, LeftSS, RightSS, and RearSS (SS stands for Semantic Segmentation).</figcaption></figure><p>into this type of output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-TQ1lbBxiQ6IEZ8aWUQ3TA.png" /><figcaption>Semantic bird’s-eye view.</figcaption></figure><p>A quick note here: the side cameras don’t really cover the whole 360° angle around the car, therefore the model needs to infer the missing regions from those actually provided at input. Also: I got the semantic segmentation for free from the CARLA simulator, this isn’t an output from any model, it’s the ground truth.</p><h3>Motivation</h3><p>To reiterate: given four side cameras with built-in semantic segmentation (we’ll discuss that in a moment) we’ll assemble the top-down view from a camera above the car. From this perspective we can immediately see the car’s surroundings, which way the road goes, and the positions of other vehicles on the road. The inspiration for this project was the <a href="https://carlachallenge.org/">CARLA Autonomous Driving (AD) Challenge</a>, in which one of the categories is based on input from four side cameras.</p><p>If you haven’t heard about the <a href="https://carlachallenge.org/">CARLA Autonomous Driving (AD) Challenge,</a> that’s OK, it’s not a prerequisite for this blog post, I’m going to introduce the important parts here anyway.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*v932hI8vM_4x3t0v.png" /></figure><p>The objective of the CARLA AD Challenge is to complete a given route (above), provided different types of input data that can be used by the algorithm driving the car. Below are the four categories, or: <em>Tracks</em>, that specify the available input:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NHMw_ViUD_yIfyvC-XY8MA.png" /><figcaption>Source: <a href="https://carlachallenge.org/">https://carlachallenge.org</a></figcaption></figure><p>Let’s look at Track 2’s description:</p><blockquote>Track 2 — RGB cameras: for brave ML teams that want to explore driving with cameras only.</blockquote><p>Brave? ML? Yup, that’s us ;)</p><p>Now, since this is a simulator, I can gather <em>virtually </em>any type of additional data I can imagine, train a neural network capable of modelling that data, and then use the model’s predictions for controlling the car, for SLAM (Simultaneous Localization And Mapping), or for path planning. In my case, the additional data was the top-down view from a camera mounted 100m<strong> </strong>vertically above the car, capable of providing ground truth semantic segmentation of the scene — I call it the <strong>semantic bird’s-eye view</strong>.</p><h3>Set-up</h3><p>OK, so if in the CARLA Challenge the available cameras are RGB, without semantic segmentation, why did I utilize it in my model? There are three reasons:</p><ol><li>It was easier to get a straight answer to the <em>fundamental question</em>: “can it be done?”</li><li>I figured that once I’ll know the answer to the above question, I’ll be able to come up with a decent semantic segmentation model, especially for the type of synthetic images acquired from a simulated environment.</li><li>Also, I figured, once I’ll have a neural network capable of mapping four semantic segmentation cameras into a single top-down view in a simulator, and that if I’d have similar side cameras mounted on a real car, and assuming that I’d have a decent model for semantic segmentation of real-world images, I would be able to re-create this semantic bird’s-eye view in the real world. Easy peasy, right? ;)</li></ol><p>Although the last reason is admittedly a bit far-fetched, it’s actually the one I personally find the most compelling.</p><h3>The architecture</h3><p>My initial thought was to use a fully convolutional autoencoder for the four input images, combine their bottlenecks in some clever manner, and to build a fully convolutional network for assembling the top-down view. But, if you think about it, fully convolutional models map a given part of the input onto a particular part of the output. Sort of like a “1-to-1” mapping. And for our purposes the architecture should allow for any part of the input to affect any part of the output. Let’s call that an “any-to-any” mapping. (See the <strong>EDIT </strong>below for a retrospective comment.)</p><p>To model this “any-to-any” mapping we need somewhere in our architecture a layer that has this magical property of carrying information from any of its input neurons to any of its output neurons. If only such layer existed…</p><p>Obviously, the dense layer works fine, and that’s what I used in the end. But before disclosing the architecture, I want to ask you this, dear Reader: have you come across an architecture that generates an image, that would involve convolutional layers, but<em> no dense layers</em>, in which any part of the input has the option of impacting any part of the output? I was thinking about an attention mechanism combined with a convolutional layer that would have this property but this solution would suffer from the same drawbacks the dense layer does.</p><p>I should probably explain my seeming animosity towards the dense layer: the number of parameters in my final model is 8,234,179, of which 7,150,080 come from the dense layers! Thus, I have a hunch there is a more natural approach. And it might utilize dense layers, by all means, but in a more clever way than I did. Maybe a factorization module is the way to go? Maybe there’s a recurrent approach? I’m open to your thoughts and suggestions in the comments section.</p><p>The model I used for inference has the following graphical representation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VBIEmQKMLUdfid8WTyPj4g.png" /></figure><p>The relevant parts are:</p><ul><li>gray: the four inputs from: <em>FrontSS</em>, <em>LeftSS,</em> <em>RightSS,</em> and <em>RearSS</em> cameras (“<em>SS”</em> stands for Semantic Segmentation, of course);</li><li>orange: the encoder_submodel<em> </em>which is a fully convolutional encoder part of an autoencoder (all inputs share the same encoder model), more about the autoencoder below;</li><li>yellow: a stack of dense layers followed by a Conv2D, then Concatenate, and then Conv2D—one stack per one input type;</li><li>orange: the decoder_submodel<em> </em>which is a fully convolutional decoder part of the same autoencoder from which we extracted the encoder_submodel (again, there is one decoder for all types of input);</li><li>cornflower blue:reconstruction being the final semantic bird’s-eye view.</li></ul><p>The inference model was built from building blocks (in particular, the encoder/decoder parts of an autoencoder) of a more complicated model trained using several auxiliary losses. Without going into details: on the one hand, the model learned how to efficiently encode and decode images from the side cameras <em>and </em>the top-down camera using a common autoencoder, and on the other hand, how to reassemble the top-down view from the encoded side cameras’ images. For an implementation check out <a href="https://github.com/asap-report/carla/blob/racetrack/PythonClient/semantic_birds_view/train_multi_model.ipynb">this</a> notebook.</p><p><strong>EDIT: </strong>as <a href="https://medium.com/u/c2e894061ac2">Bartosz Topolski</a> pointed out, it’s possible to use a fully convolutional architecture that has this “any-to-any” property. You just need to make sure that the filter size of the convolutional layer after the autoencoder’s bottleneck is large enough (with respect to the bottleneck’s size). I’ve checked and he’s right, <a href="https://github.com/asap-report/carla/blob/racetrack/PythonClient/semantic_birds_view/train_multi_model_alternative.ipynb">this</a> is a notebook with a model that doesn’t use dense layers.<strong> </strong>This solution doesn’t have some of the properties that a “standard” fully convolutional model has (you can’t expect an input of a different shape to be processed correctly), but it stands to prove that the current architecture can be improved. For a 3-class variant (details below) my model had a loss of ~0.062, and Bartek’s had ~0.042 on the test set! And on the 7-class variant mine had ~0.40 and Bartek’s had ~0.36.</p><h3>Purpose</h3><p>A top-down view has a number of advantages:</p><ul><li>it condenses relevant information from four images into one;</li><li>it can serve as a building block for localization/mapping algorithms;</li><li>it can be used for path planning (example of which I’m presenting below);</li><li>it allows for training a higher-level reinforcement learning agent that chooses an optimal path rather than atomic actions (a bit longer comment in a moment).</li></ul><p>I’m going to show results of a crude path planning procedure at the end of this post. I’ll probably devote a future blog post to the topic of possible applications of the method presented here.</p><h3>Training and Results</h3><h4><strong>Training</strong></h4><p>I’ve trained the model on data gathered in the two default towns available in CARLA (<em>Town01 </em>and <em>Town02</em>), 40 episodes per town, each episode 1000 frames long. For validation and testing I used separate episodes but from the same towns. All results shown here were produced on the test episodes.</p><p>I’ve gathered images with resolution 300 × 200 (same as in the CARLA AD Challenge) but because the training took <strong>a lot</strong> of time, I’ve decimated them by selecting every other row/column, and then cropped them so that the shape would be divisible by 8 (requirement of the fully convolutional autoencoder), so the final shape was 144 × 96. In this set-up the training took ~16h on my GTX 1080 Ti. Inference on the same machine takes 4.5ms, however note that in a full setup (from RGB to semantic bird’s-eye view) we would also need some time for producing semantic segmentation for the input images.</p><h4><strong>Semantic bird’s-eye view</strong></h4><p>Here’s a compilation of the input side cameras, ground truth, and the predicted top-down view (entire results can be found <a href="https://www.youtube.com/playlist?list=PLCQsNy3R3bfo7rcQuHfTWcOAH2m5bnOMO">here</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*lM59E3-VqztIXDlasUFGaw.gif" /><figcaption>Semantic bird’s-eye view for 7 classes: (roads, road lines), (sidewalks), (buildings), (fences, pedestrians, poles, walls, other), (vehicles), (vegetation), (none).</figcaption></figure><p>The rightmost image was constructed by taking an argmax of the predicted class probabilities. This approach has an annoying drawback: the vehicles seem to be reappearing out of nowhere, even though actually the predicted probability for their class is not null. The model was slightly imbalanced and I could have tried <a href="https://scikit-learn.org/stable/modules/calibration.html">calibrating</a> it, and/or instead of using an argmax try to define a better decision making procedure (in terms of a hand-crafted <a href="http://users.isr.ist.utl.pt/~wurmd/Livros/school/Bishop%20-%20Pattern%20Recognition%20And%20Machine%20Learning%20-%20Springer%20%202006.pdf#%5B%7B%22num%22%3A193%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22Fit%22%7D%5D">loss matrix</a> like the one you build in decision theory). Also, in the following subsection I’m using a better technique of visualizing the predicted class probabilities. Long story short, the visuals might have been tweaked, but I didn’t want to spend too much time on it.</p><p>Instead, I’ve decided to spend some time on utilizing this predicted top-down view for path planning.</p><h4><strong>Crude path planning</strong></h4><p>Now, the truth is I only need three classes to be able to drive around and not collide with others: road, vehicles, everything else. Here’s how these results look like (the full video can be found <a href="https://youtu.be/vngJ2Auzlw8">here</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*cYOhh-4jBAsZIFugyokcwQ.gif" /><figcaption>Semantic bird’s-eye view for 3 classes: (roads, road lines), (vehicles), (everything else).</figcaption></figure><p>Again, the predicted vehicles materialize very close to our car, but it so happens that because now there are three classes, instead of taking an argmax, I can treat these images as RGB so that we can better see that the model indeed predicts other vehicles.</p><p>It’s best to see what I mean:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*uTLsLsNSUbg3RlBiP1gOkQ.gif" /></figure><p>The white dots denote the waypoints of the path planning procedure. The path is found using a greedy algorithm that scans a range of possible waypoints, takes a sphere centered on a given waypoint, and calculates the “average road” for that sphere. Then, the waypoint with the highest “average road” is chosen, and the procedure is repeated. For implementation see <a href="https://github.com/asap-report/carla/blob/racetrack/PythonClient/semantic_birds_view/utils.py">this</a> script, the function is called find_waypoints.</p><p>I should emphasize: these dots were added <em>after</em> the data collection process, which is the reason why sometimes the waypoints “can’t make up their mind” and jump from one option to a completely different one. But if the car actually followed the path, such cases would not occur.</p><p>The path planning procedure generates waypoints that can then be followed using some standard control procedure, MPC for example (check out my <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">previous blog post</a> to learn more about the MPC).</p><h3>Discussion</h3><p>The results for the 7-class variant look a bit trippy, I must say. The primary reason is that the input doesn’t contain the whole information required for reconstructing the top-down view. Heck, the side cameras don’t provide a full 360° sweep around the car. But also: certain classes are rare and/or form shapes that are thin and I’ve noticed that these types of shapes get lost in the encoding process. U-Net, for example, handles such detailed shapes better because it uses skip connections. I don’t quite know how these skip connections would fit into my current model, but it might be worth looking into.</p><p>The 3-class variant is less wobbly in part because the location of objects is more “inferable” from the input, and in part because these three classes are admittedly easier to predict. By “inferable” I mean: besides vehicles, there’s very little that can happen in the narrow regions not covered by any of the side cameras, the model can easily guess that.</p><p><strong>The data set </strong>is available <a href="https://my.pcloud.com/publink/show?code=kZMTOU7ZHJd4qLXBqxfQFzJ6EcT0euo3fjKy">here</a> and the <strong>code</strong> <a href="https://github.com/asap-report/carla/tree/racetrack/PythonClient/semantic_birds_view">here</a>, if you’d like to experiment with the architecture and, ultimately, to come up with a better architecture and results. If you encounter problems with the code, please create an Issue in the repository.</p><h4><strong>Future work</strong></h4><p>I suspect that the 7-class-model might yield better results if there was additional information about how tall the objects are. So I think it makes sense to utilize the depth map in the top-down camera as part of the output predicted by the model.</p><p>Instead of using a single top-down camera, I’m thinking of using several, suspended at different heights. This would result in a Matryoshka-like set of outputs and should allow for predicting the road and other vehicles at greater distances than currently.</p><p>I have a hunch that a generative model might do a better job of “guessing” the parts of the output that don’t have their corresponding parts in the input. This applies to the regions not covered by the side cameras as well as those situations in which a nearby vehicle blocks the view.</p><p>Because the crude path planning procedure looks promising, I feel tempted to try out a reinforcement learning agent that would choose a path, perhaps the optimal speed, and (potentially) coefficients of a controller. Then, the path would be followed using the controller, as it’s typically done, but the coefficients of the controller would be dynamically changed by the agent. I’m hoping here that when operating on a higher level, the agent would have a lower chance of overfitting to atomic decisions, and instead learn more general laws governing the environment. Sorry if this paragraph is less clear, I’ll probably devote a future blog post where I’ll explain myself a little better.</p><p>Thus far I’ve ignored pedestrians (I’ve excluded them from the simulation) and I must either find a better way of modelling rare classes (e.g. weigh their contributions to the loss function, come up with a better architecture) or emphasize their importance by synthetically enlarging their presence in the top-down view. Actually, come to think of it, making every pedestrian morbidly obese seems to be the right way to go.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*Wcwoe0AwNMyZVEX1xztv2A.png" /></figure><h3>References</h3><p>In this work the authors solve a similar problem but using homography:</p><p><a href="https://arxiv.org/abs/1801.00708">[1]</a> Deng, L., Yang, M., Li, H., Li, T., Hu, B., &amp; Wang, C. (2018). Restricted deformable convolution based road scene semantic segmentation using surround view cameras. <em>arXiv preprint arXiv:1801.00708</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*Wcwoe0AwNMyZVEX1xztv2A.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1e636741af3f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/from-semantic-segmentation-to-semantic-birds-eye-view-in-the-carla-simulator-1e636741af3f">From semantic segmentation to semantic bird’s-eye view in the CARLA simulator</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to the CARLA simulator: training a neural network to control a car (Part 2)]]></title>
            <link>https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/6a71115f2940</guid>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[robotics]]></category>
            <category><![CDATA[autonomous-cars]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Fri, 15 Mar 2019 06:20:37 GMT</pubDate>
            <atom:updated>2022-01-03T14:48:21.138Z</atom:updated>
            <content:encoded><![CDATA[<p>Training neural network models on data gathered with two deterministic controllers and my non-deterministic self.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Sh3wmNlPCxN1xUqG6gU3tg.png" /></figure><p>Before we start, the source code for this whole project is available <a href="https://github.com/asap-report/carla/tree/racetrack">here</a>. If you have questions regarding the code, please create an Issue. And if you’d like to gain access to the data to re-produce the results, leave a comment, and I’ll try my best to put it out somewhere.</p><p><strong>EDIT: </strong>in a more recent blog post (<a href="https://medium.com/@ponadto/drive-faster-with-machine-learning-c8d40106a1f4">here</a>) I’ve decided to use a much more lightweight environment than CARLA to train a model that I used to drive *faster* than the algorithm used for driving the car for data collection.</p><h3>Introduction</h3><p>In the <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-1-e1c2c9a056a5">previous part of this series</a>, I trained models on depth maps (rather than RGB) collected from the CARLA simulator <a href="http://proceedings.mlr.press/v78/dosovitskiy17a/dosovitskiy17a.pdf">[1]</a>. (There’s a good reason for this and I’ll discuss it at the end of this blog post.) However, while the essence of Part 1 was: <em>how to create your own race track in CARLA and get a neural network to control a car to go around it</em>, the gist of Part 2 is: <em>how the source of data for training neural network models influence performance on the race track</em>.</p><p>This time around I’ve used a different car, one that is faster, more responsive and (subjectively) more fun to control —the Audi TT. The default car (Ford Mustang) has lower acceleration and its tires rarely loose traction. And I wanted the model to have a chance to learn on data in which slippage occurs so that it would, at least in principle, “know how to handle” such situations.</p><p>I’ve also added three new race tracks. I’ll go through them in more detail below, but what’s important for now is that I used first two race tracks (creatively named: “01” and “02”) for training, and the other two (“03” and “04” , you guessed it) for testing. The measure of a model’s performance was the distance (expressed in laps around the track) accomplished in 10.000 steps of CARLA’s simulation.</p><h3>Data</h3><p>I used three methods of collecting data for training the models:</p><ol><li>The Proportion-Derivative (PD) controller introduced in Part 1;</li><li>The Model Predictive Control (MPC) which we’ll discuss shortly;</li><li>The Game Pad (GP), i.e. I trained the model to mimic my driving behavior.</li></ol><p>Here are examples of the three methods of gathering data, as recorded on race track “01”.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FjNTuD1JQa80%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DjNTuD1JQa80&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FjNTuD1JQa80%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/fda60a6e42ab9d4242c7896f52bdfb50/href">https://medium.com/media/fda60a6e42ab9d4242c7896f52bdfb50/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F280ZqYsj6pA%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D280ZqYsj6pA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F280ZqYsj6pA%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/f7baa296eba200bb0009ab1c2daad2c4/href">https://medium.com/media/f7baa296eba200bb0009ab1c2daad2c4/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fx1fIhzvwoNc%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dx1fIhzvwoNc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fx1fIhzvwoNc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/1e776e8bad2707551ac5572b77e11400/href">https://medium.com/media/1e776e8bad2707551ac5572b77e11400/href</a></iframe><p>Now, what’s crucial is that these simulations were run in <strong>synchronous mode </strong>(the simulation waits for the model to make a prediction), which gave an unfair advantage to the MPC. I’ll go into more details of what an MPC is but suffice it to say that it’s computationally demanding. Notice that in the movie above the average FPS (Frames Per Second) for MPC is roughly 14. So it takes about 70ms to produce a prediction. That’s partly because I implemented the MPC in Python, and partly because it simply is more complex than, for example, the PD controller. Running MPC (and other models for that matter) in asynchronous mode would require more work and I’ll need to look into that, but for now that wasn’t my focus.</p><p>Also, to “augment” the data, I’ve added uniform noise to steering and throttle of all the controllers. The values used for training were the clean, unspoiled values returned by a controller, then noise was added and sent to CARLA.</p><p>As a side note, the weather is only an aesthetic effect, it did not influence tire friction, nor the depth data on which the neural network models were trained on. I used weather conditions as an indicator of which model was used to control the car. The convention was: sunny→Proportion-Derivative controller, cloudy→Model Predictive Control, rainy→Game Pad, sunny late afternoon→Neural Network.</p><h3>Control</h3><p>In this section, I discuss the four methods of controlling the car used in this experiment:</p><h4>PD controller</h4><p>This type of control was already introduced in <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-1-e1c2c9a056a5">Part 1</a> of this series, so I’m not going to spend much time on it here. Shortly speaking, the steering angle at time <em>t</em> is chosen so as to counter the deviation from the pre-defined path — the so-called <em>Cross-Track Error</em>, CTE. The steering angle is composed of two terms, one proportional to CTE, and the other proportional to the time derivative of CTE.</p><p>This is the most simplistic version of the PD controller and it served nothing other than providing a baseline. Only the steering was being controlled; the throttle was brutally clipped, keeping the velocity at around some chosen value. Not only that, this type of controller doesn’t make use of the available information provided in the pre-defined path, it only looks at the local state and naively tries to correct it.</p><h4>MPC</h4><p>The Model Predictive Control is something different entirely. The idea is to predict how the system (in this case: the car) will behave in the next <em>N </em>steps, assuming a simplistic model of the world (in this case: a kinematic model of motion). The MPC returns <em>actuators</em> (steer and throttle) for the next <em>N </em>steps, but only actuators predicted for the next time step are used for control.</p><p>The values of the actuators are determined by minimizing a <em>cost function </em>(I’m only including terms relevant for this discussion, the ellipsis denotes “other terms”):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Eq0f6EDWG02AgYbVabocg.png" /></figure><p>where δ and <em>a </em>are the steer angle and throttle (actually, acceleration), the {<em>cᵢ</em>}<em> </em>are coefficients chosen by the user, and v₀ is the target speed with which we want the car to drive. The type and number of terms differs between implementations, but what’s important is that:</p><ul><li>on the one hand we want to keep CTE small (follow the path), but on the other we want to go with speed v₀;</li><li>the cost function is composed of terms spanning <em>N </em>steps into the future.</li></ul><p>The MPC minimizes the <em>cost </em>subject to a set of constraints dictated by the kinematic model, which makes this a nonlinear optimization problem. In the <a href="https://github.com/asap-report/carla/tree/racetrack">accompanying repo</a> I use the SLSQP (Seqential Least SQuares Programming) method available in the scipy.optimize.minimize function.</p><p>Once we find an optimal set of actuators, {(δ<em>ᵢ</em>, <em>aᵢ</em>)}, only the first pair, (δ₁, <em>a</em>₁), is used to control the car. Now, because the <em>cost</em> includes conflicting terms (follow the path, but go as fast as possible), the trajectory generated by MPC is often similar to that of an apt driver. The car approaches some of the corners wide, without much slowing down, some require the car to really slow down, and some (like chicanes, for examples) are elegantly cut.</p><p>I recommend <a href="https://www.youtube.com/playlist?list=PLn8PRpmsu08ozoeoXgxPSBKLyd4YEHww8">this series of videos</a> for a good yet gentle introduction to MPC.</p><h4><strong>GP</strong></h4><p>To control the car, I used the Logitech F310 game pad and the pygame module to pass on the signal from the pad to CARLA. I should note that at first, I tried to go as fast as possible — cut corners, drift through some of the toughest turns, and reach 100km/h on the longest straights. The video provided earlier shows exactly that style of driving. But despite my best efforts, I was unable to train a neural network to mimic this type of driving, or even: to train the model so that it would successfully complete at least one lap on any of the race tracks.</p><p><strong>So I cheated. </strong>I drove the car in such a way that the model had a chance of learning a certain pattern of behavior. This is how it looked like:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FywPnqYxY_Gc%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DywPnqYxY_Gc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FywPnqYxY_Gc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/84ac39d514287b6088413d94753e9593/href">https://medium.com/media/84ac39d514287b6088413d94753e9593/href</a></iframe><p>The pattern of behavior that I wanted to “teach” the model was to follow the track along the barriers. Still, fitting a neural network to data produced by a human is hard, and we’ll discuss this a bit more in the <strong>Discussion</strong>.</p><h4>The neural network model</h4><p>I used a singular neural network architecture, which is a composition of two sub-models: an embedder, and a multi-task set of output layers. The embedder is a LeNet-like-submodel (similar to the one used in Part 1) used to yield a <em>feature extraction layer </em>or: an <em>embedding layer</em>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0211605390d5d8471bf8556a4d8759e7/href">https://medium.com/media/0211605390d5d8471bf8556a4d8759e7/href</a></iframe><p>The second part of the model builds on top of the feature extraction layer. First are the steering angles —each has its own hidden layer, followed by a single prediction neuron. Next, the embedding layer is concatenated with the speed (assumed to be provided as input), and the outputs for the steering angles. Here’s how it looks like in code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f7134cbf580aa95cbd8b7c8d838fd6b4/href">https://medium.com/media/f7134cbf580aa95cbd8b7c8d838fd6b4/href</a></iframe><p>Why like this? Why not build prediction for throttle and steering on top of an embedding layer already concatenated with speed?</p><p>When I provided speed as input for both steering and throttle, the model tried to cut corners too sharply:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAHkx74Hc8Vk%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAHkx74Hc8Vk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAHkx74Hc8Vk%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ed1cafdf0e46ce03f06a7a2a7e183de4/href">https://medium.com/media/ed1cafdf0e46ce03f06a7a2a7e183de4/href</a></iframe><p>It seems as if the model learned that low speed = sharp turn. When I tried to tweak the architecture (reduce complexity, in particular) I ran into a different problem: the model didn’t turn the car sufficiently strong when the speed was high. (However, in the Discussion section I mention <a href="https://arxiv.org/pdf/1709.07174.pdf">an article</a> that uses an architecture in which speed <em>is </em>an input for steer predictions, and their results look pretty impressive!)</p><p>But speed is important for making predictions of the throttle. Thus, the architecture detailed above is my workaround for this problem. Also, I’ve added the steering angle outputs to the embedding layer in hopes of providing extra data for the throttle, but frankly, I don’t think it helped much. Same as with steering, each throttle got its own hidden layer, followed by a prediction neuron.</p><p>Admittedly, a dedicated hidden layer for each of the outputs significantly increases complexity of the model and I needed to keep a wary eye on overfitting. A more natural approach would be to use a single, common hidden layer emb for all steering outputs and a <br> concatenate([emb, inp_speed] + steer_outputs) <br>layer for all throttle outputs. And such a model performed fine, but the more complex model managed tight corners better at higher speeds.</p><p>I’ve tried to generate an informative visualization of the architecture, but this is what I got using keras:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g6rgyk9AMINSNyTrhKu6iQ.png" /></figure><p>And I’ve <em>really</em> tried to make it more informative by using other tools, but didn’t get much improvement. So, like in Part 1, I claim that when it comes to explaining the model the code does a better job than visualizations.</p><h3>Race tracks</h3><p>I’ve generated three more maps the same way as described in Part 1. I first created race track “02” and on it tested a model build on “01”. It failed miserably; this new track was too tangled up. When I trained on “02” and tested on “01” the model also failed. So I decided to train on (“01”, “02”), and created race track “03” for testing, and the model performed OK. Once I finally decided on the architecture, the training process, etc., I added race track “04” to the test set to avoid selection bias, and the final set-up was: train on (“01”, “02”), test on “03” and “04”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/774/1*sw7WYH8XJoGMGpARKUu0ew.png" /></figure><h3>Results</h3><p>Before jumping into the actual results, two important pieces of information:</p><p><strong>1. This is not a thorough study</strong></p><p>What I mean by that is that I chose a particular architecture not by systematically evaluating a set of candidates. For example, the multi-task predictions for 10 steps into the future— that’s something I <em>think </em>stabilizes the predictions, makes the ride smoother. But just before publishing this post, I tested how well a model without those predictions performed on race track “04” and it actually did OK. It got worse results, but I can’t say for sure it wouldn’t achieve similar or even better results if I were to devote enough time tweaking it.</p><p><strong>2. I had two knobs for controlling the pace of the car</strong></p><p>The correlation between predicted vs. true values of throttle for almost all of the models looked like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*SZ5cxa7SLFJUWCerYhtFEw.png" /></figure><p>There is <em>some </em>correlation but this is a mediocre performance. Oftentimes, a car wouldn’t even start going because at the initial state the throttle prediction would be too low. To deal with that, I introduced two coefficients, <em>α</em> and <em>β</em>, such that the throttle prediction <em>p</em> was transformed into (<em>α</em>·<em>p + β</em>)<em> </em>before passing it to the CARLA client. This allowed me to squeeze better performances out of all of the models.</p><p>But it also introduced human error into the results. A systematic approach would be to run a grid search for a large number of combinations of <em>α </em>and<em> β</em>, and report the best result acquired in this fashion. However, I only had my home PC to evaluate the models, I had to cut corners (excuse the pun).</p><p>The following table sums up the performances of the controllers and the neural network models on the four race tracks:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L1-jAWDc9YUXrvWB5QmU_Q.png" /><figcaption>Table 1: Average distances traveled in one episode (10000 steps of simulation) expressed by the number of laps on their respective race track. The data are averaged over 20 episodes, and the averages are supplemented with standard deviations of the results. Note that each race track has a different length so it only makes sense to compare results for each race track (column). The fastest controller and neural network model are shown in bold.</figcaption></figure><p>I need to explain the ⟨NN(.)⟩ model. Remember, the neural network was trained to output the next (steer, throttle), and 10 their values into the future. I’ve mentioned that the more complex model performed better, and that I “needed to keep a wary eye on overfitting”. I figured that I can reduce variance by introducing a low-cost ensemble by simply bookkeeping the predictions for time step <em>t </em>made in the previous 10 steps and then averaging them. That is: I kept predictions for time step <em>t </em>made<em> </em>at moments <em>t-10, t-9, t-8, …, t-1, </em>and<em> t, </em>and then averaged them.</p><p>In what follows, I’m focusing on those ⟨NN(.)⟩ models, since they seem to perform at least as well their “pure” counterparts.</p><p>The best controller is MPC but that’s no surprise. What’s interesting is that the ⟨NN(MPC)⟩ model performs only slightly better than ⟨NN(GP)⟩ even though GP performs much worse than MPC. It’s interesting to see how these two models control the car on race track “04”:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fhc1zbTDqyHw%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dhc1zbTDqyHw&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fhc1zbTDqyHw%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/22340512d16aa35181125b6206b80ea5/href">https://medium.com/media/22340512d16aa35181125b6206b80ea5/href</a></iframe><p>That’s smooooth! I really like the way the car slows down in critical moments, and rapidly accelerates after managing them. And remember, this is this model’s first time on this race track.</p><p>Now, the model trained on the game pad data:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FqWWOoNZanXU%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqWWOoNZanXU&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FqWWOoNZanXU%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/76474069e46c14549f617ca875d3e399/href">https://medium.com/media/76474069e46c14549f617ca875d3e399/href</a></iframe><p>Notice how wobbly the car behaves, and also: how on occasion the model decides to slow down for no apparent reason. There’s something “wrong” with this model, and I comment on it in the Discussion.</p><p>And, for completeness sake, let’s see how the ⟨NN(PD)⟩ model does on the same track:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FZsvCG_hslRk%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DZsvCG_hslRk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FZsvCG_hslRk%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c8b50dbee495ee04dfa6b649c8e567a6/href">https://medium.com/media/c8b50dbee495ee04dfa6b649c8e567a6/href</a></iframe><p>There’s a suitable meme summarizing the performance of the ⟨NN(PD)⟩ model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J5LYo12S3jgOmbnX2-lTCA.png" /></figure><h3>Discussion</h3><p>It seems that the results for ⟨NN(MPC)⟩ are better than for NN(MPC), but in the case of ⟨NN(GP)⟩ vs. NN(GP) the gain is practically nonexistent. I conclude that the reason why ⟨NN(MPC)⟩ performs better than NN(MPC) is because NN(MPC) suffers from overfitting and the ensemble indeed helps reduce the variance of the model. On the other hand, because the ensemble didn’t help the NN(GP) model, it seems that overfitting is not this model’s main problem.</p><p>What is, then?</p><p>I’ve tried to reduce bias by crafting more and more complex additions to the architecture. There were many of them, too many to list here, but I should note: the reason why the final model is so darn complicated is a corollary of this search for bias reduction.</p><p>In the end, I failed to reduce bias, I don’t think that’s why NN(GP) is so wobbly and indecisive.</p><p>In his lecture (I highly recommend the whole course!):</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fvideoseries%3Flist%3DPLkFD6_40KJIxJMR-j5A1mkxK26gh_qg37%26start%3D2429&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DyPMkX_6-ESE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FyPMkX_6-ESE%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4e764cbcd59c0a73cc67fb8f1b6802e/href">https://medium.com/media/b4e764cbcd59c0a73cc67fb8f1b6802e/href</a></iframe><p>Sergey Levine gives two good reasons for why training a model on data collected from a human expert is problematic:</p><ol><li>Non-Markovian behavior;</li><li>Multimodal behavior.</li></ol><p>The question is: why didn’t it prevent me from successfully fitting a model on the game pad data? There are two main reasons:<br>1. The test race tracks are not that different from the training ones. <br>2. I claim (I haven’t checked that, I would need to see how well a model trained on RGB performs) that the depth images help reduce the gap between training and test distributions (check out <a href="https://youtu.be/xZKj7Z1CwHc?list=PLkFD6_40KJIxJMR-j5A1mkxK26gh_qg37&amp;t=1122">this lecture’s</a> derivation for more details).<br>Still, I wonder if any of the architectural decisions (predicting future steps, in particular) helped alleviate at least the “multimodal behavior” problem.</p><p>Also, from a reinforcement learning perspective, in <a href="https://arxiv.org/abs/1709.07174">[2]</a> we read (IL stands for “Imitation Learning”):</p><blockquote>Overall, when using human demonstrations, online IL can be as bad as batch IL, simply due to inconsistencies introduced by human nature</blockquote><p>Check out that article and also <a href="https://autorally.github.io/">these materials</a> provided by the good people at Georgia Tech — they are truly worth your while! And also, on the topic of this article, this is their architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6ZNBAHDC5IKH4J6Qa5DMjA.png" /></figure><p>It looks like providing speed as input for steering predictions didn’t lead to the problems I’ve experienced. I’m guessing that if I spent more time tweaking the architecture, I would eventually get satisfactory results.</p><h4>Model selection</h4><p>Like in Part 1, I struggled with model selection. To illustrate my frustration, let me give you a puzzle.</p><p>Here are three scatter plots of the predicted vs. true steering angles, your task is to guess which of the models: NN(PD), NN(MPC), NN(GP), they belong to:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mVTDb5Js8zmcUtLx73B4bA.png" /></figure><p>OK, I think we can all agree that NN(MPC) was the best model, NN(GP) was second, and NN(PD) was third. Based on that, it’s easy to guess that <strong>C </strong>is the NN(MPC) model, that’s the scatter plot with the highest correlation on the test set.</p><p>I found it a bit confusing that a model with scatter plot <strong>A </strong>was able to finish even a single lap. Let me explain: it seems that this model correctly predicts the sign of the steering angle (e.g. if the steer angle was less than zero the model generally returned a value less than zero), but the actual magnitude of the prediction looks a bit more like an output from a call to random.uniform(-1, 0) (actually, it’s not, the points are not distributed uniformly). It seems especially worrisome for critical situations, in which the car needs to perform a sharp turn, and from scatter plot <strong>A</strong> it would seem that in such situations the model can return a value close to zero.</p><p>Yet, scatter plot <strong>A </strong>belongs to the NN(GP) model, and <strong>B </strong>to the NN(PD) model. Even though instantaneous predictions are flawed, the NN(GP) model is able to beat corner after corner, and finally the whole track. But more then that, the NN(GP) model in reality (and by “in reality” I mean “in simulation”) performs significantly better than a model with higher correlation between true / predicted steer values — the NN(PD) model.</p><p>I should note that I selected the best model based on MSE on the test set (by the way, the test set in this context is <em>not </em>race track “03” and “04”, but rather laps around race tracks “01” and “02” not used for training). Instead, perhaps evaluating the models on whole corners (sequences of steering angles) would be a better strategy? I should look into offline policy evaluation strategies, because my current procedure has glaring flaws.</p><h4>Future work and why use depth images instead of RGB</h4><p>A high-level list of ideas I’m planning to explore:<br>1. Get the network to predict the speed, not throttle, and use a controller for throttle to achieve that speed (might not work if changes in speed require the controller to be able to adapt quickly).<br>2. Other / better controllers—<a href="https://pythonrobotics.readthedocs.io/en/latest/modules/path_tracking.html#pure-pursuit-tracking">here’s</a> a good source of implementations; also<a href="https://arxiv.org/abs/1811.01848"> POLO</a> looks like an interesting avenue, but additionally: I have an idea (partly inspired by POLO) of supplementing the cost function in the MPC with “learnable” terms making the simple kinematic model a bit more realistic.<br>3. Find better strategies of evaluating the models offline.<br>4. Run CARLA in asynchronous mode.</p><p>And why use depth images rather than RGB? This is my miniature car:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VtuAOP1w6HL6D-1L7i709A.jpeg" /></figure><p>I called it <em>Karr</em> (to pay homage to <a href="https://en.wikipedia.org/wiki/KARR_(Knight_Rider)">KARR</a>) and it’s loosely based on the <a href="http://f1tenth.org/car-assembly">F1/10th specification</a>. Those two sensors in the front—that’s <a href="https://www.stereolabs.com/zed/">ZED</a> and <a href="https://click.intel.com/intel-realsense-depth-camera-d435i-imu.html">Intel RealSense D435i</a>—two stereo cameras that provide depth images. The processing unit is the Jetson TX1 with its own GPU, capable of fairly fast inference using neural networks.</p><p>My plan is to transfer knowledge from the simulation to the real world and to use a model trained in CARLA to drive around a race track with Karr. Other have already tried this using RGB data (see for example <a href="https://arxiv.org/abs/1801.03458">[3]</a> and <a href="https://arxiv.org/abs/1812.03823">[4]</a>), but I have a sneaking suspicion that depth images may suffer from a narrower “simulation-to-reality gap”.</p><p>But also, depth images, if treated like point clouds, allow for a range of informative linear transformations for augmenting data (like translations and rotations), providing a better representation of the data to a neural network (making use of the <a href="https://nikolasent.github.io/opencv/2017/05/07/Bird%27s-Eye-View-Transformation.html">bird’s eye view</a> but better), and localizing the car on the track.</p><p>I don’t know yet what I’m going to write about in Part 3, but there’s plenty to choose from:<br>1. Karr and domain adaptation: fighting the simulation-to-reality gap.<br>2. A more detailed look into CARLA and Unreal Engine 4.<br>3. Intro to robotics and depth cameras.<br>If you have a pick I would love to hear from you in the comments below!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*W8CunTbB00ksIgYy8wCa2A.png" /></figure><h3>References</h3><p><a href="http://proceedings.mlr.press/v78/dosovitskiy17a/dosovitskiy17a.pdf">[1]</a> Dosovitskiy, A., Ros, G., Codevilla, F., Lopez, A., &amp; Koltun, V. (2017). CARLA: An open urban driving simulator. <em>arXiv preprint arXiv:1711.03938</em>. <br><a href="https://arxiv.org/abs/1709.07174">[2]</a> Pan, Y., Cheng, C. A., Saigol, K., Lee, K., Yan, X., Theodorou, E., &amp; Boots, B. (2017). Agile off-road autonomous driving using end-to-end deep imitation learning. <em>arXiv preprint arXiv:1709.07174</em>.<br><a href="https://arxiv.org/abs/1801.03458">[3]</a> Yang, L., Liang, X., Wang, T., &amp; Xing, E. (2018). Real-to-Virtual Domain Unification for End-to-End Autonomous Driving. In <em>Proceedings of the European Conference on Computer Vision (ECCV)</em> (pp. 530–545).<br><a href="https://arxiv.org/abs/1812.03823">[4]</a> Bewley, A., Rigley, J., Liu, Y., Hawke, J., Shen, R., Lam, V. D., &amp; Kendall, A. (2018). Learning to Drive from Simulation without Real World Labels. <em>arXiv preprint arXiv:1812.03823</em>.</p><h3>Other cool resources</h3><ul><li><a href="https://towardsdatascience.com/learning-to-drive-smoothly-in-minutes-450a7cdb35f4">This blog post</a> is definitely worth checking — I can’t wait for the next part from <a href="https://towardsdatascience.com/@araffin">Antonin Raffin,</a> I am a fan of everything he’s put out so far.</li><li><a href="https://ai.googleblog.com/2018/11/a-structured-approach-to-unsupervised.html">This blog post</a> and the accompanying <a href="https://arxiv.org/pdf/1811.06152.pdf">article</a> about depth prediction (aka <br>“depth sensing”) using monocular videos.</li><li><a href="https://bair.berkeley.edu/blog/2018/10/23/depth-sensing/">Depth sensing at Berkley</a>.</li><li>And finally, definitely check out the <a href="https://carlachallenge.org/">CARLA Autonomous Driving Challenge</a>.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*W8CunTbB00ksIgYy8wCa2A.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6a71115f2940" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">Introduction to the CARLA simulator: training a neural network to control a car (Part 2)</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to the CARLA simulator: training a neural network to control a car (Part 1)]]></title>
            <link>https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-1-e1c2c9a056a5?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/e1c2c9a056a5</guid>
            <category><![CDATA[control]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[autonomous-vehicles]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[autonomous-cars]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Tue, 27 Nov 2018 09:55:29 GMT</pubDate>
            <atom:updated>2022-01-03T14:46:44.401Z</atom:updated>
            <content:encoded><![CDATA[<p>Intro to CARLA + simple controller for collecting data + training a neural network for steer control.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kQGvDwofTLzTIfH9iUm1jA.png" /></figure><p><strong>Introduction: </strong>the CARLA simulator is a platform for testing out algorithms for autonomous vehicles.<strong><br>Motivation: </strong>I wanted to check out CARLA, build a simple controller for following a predefined path around the track to gather data, and then train a neural network model using that data.<br><strong>Approach: </strong>I used a simple CNN architecture and modeled the steering angle.<strong><br>Results: </strong>the model is able to drive the car around the track by controlling steering.<strong><br>Project: </strong><a href="https://github.com/asap-report/carla/tree/racetrack/PythonClient/racetrack">https://github.com/asap-report/carla/tree/racetrack/PythonClient/racetrack</a></p><p><strong>EDIT: </strong>in a more recent blog post (<a href="https://medium.com/@ponadto/drive-faster-with-machine-learning-c8d40106a1f4">here</a>) I’ve decided to use a much more lightweight environment than CARLA to train a model that I used to drive *faster* than the algorithm used for driving the car for data collection.</p><h3>CARLA</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FHp8Dz-Zek2E%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DHp8Dz-Zek2E&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FHp8Dz-Zek2E%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/2596f9e478cdd7032d05472162e1681f/href">https://medium.com/media/2596f9e478cdd7032d05472162e1681f/href</a></iframe><p>CARLA is an open-source simulator built on top of the Unreal Engine 4 (UE4) gaming engine, with additional materials and features providing:</p><ul><li>a <a href="https://carla.readthedocs.io/en/stable/cameras_and_sensors/#ray-cast-based-lidar">LIDAR</a></li><li>a depth map (emulating a stereo camera, e.g.: <a href="https://www.stereolabs.com/">ZED</a> or <a href="https://click.intel.com/intelr-realsensetm-depth-camera-d435.html">Intel’s D435</a>)</li><li>semantic segmentation data</li><li>two (and soon to be three) towns with pedestrians, other cars, traffic lights, speed limits, and realistic layout</li><li>a <a href="https://github.com/carla-simulator/imitation-learning">repository</a> and <a href="https://drive.google.com/file/d/1hloAeyamYn-H6MfV1dRtY1gJPhkR55sY/view">data</a> for <a href="http://vladlen.info/papers/conditional-imitation.pdf">imitation learning</a></li><li>a <a href="https://github.com/carla-simulator/reinforcement-learning">repository</a> for testing out reinforcement learning models</li><li>an option of running <a href="https://carla.readthedocs.io/en/stable/carla_headless/">without display</a></li></ul><p>But more importantly, CARLA is <a href="http://carla.org/posts/">thriving</a>; it has a <a href="https://discord.gg/8kqACuC">great community</a>, it’s documented well, and it’s (subjectively) easy to use.</p><h4>Unreal Engine 4</h4><p>One of the reasons I became interested in CARLA was the fact that it’s built using UE4. I wanted to hone (or rather: keep alive) my C++ skills, and at the same time tinker with machine learning for autonomous vehicles, so CARLA was the perfect combination of these two worlds.</p><p>But something I didn’t anticipate is that UE4 allows for developing quite advanced features without the necessity of implementing them in C++. For that, UE4 has a dedicated visual scripting language called <a href="https://docs.unrealengine.com/en-us/Engine/Blueprints">Blueprints</a>. I’ve used Blueprints for the creation of the race track (more details further).</p><h4>Alternatives to CARLA</h4><p>There are several open-source simulators for autonomous cars and/or racing, but very few with capabilities comparable to or greater than CARLA’s. Here’s a list of those that I’ve considered (with their respective environments in parentheses):</p><ul><li><a href="https://github.com/CPFL/Autoware">Autoware</a> (Gazebo)</li><li><a href="https://github.com/Microsoft/AirSim">AirSim</a> (UE4 &amp; Unity)</li><li><a href="https://sourceforge.net/projects/torcs/">TORCS</a> (OpenGL)</li></ul><p>And two simpler ones:</p><ul><li>the <a href="https://github.com/udacity/self-driving-car-sim">Udacity simulator</a> (Unity)</li><li><a href="https://docs.donkeycar.com/guide/simulator/">Donkeycar simulator</a> (Unity)</li></ul><p>In the end, I only considered CARLA and AirSim since I wanted to work with UE4, and chose CARLA because it seemed easier to contact with the community <em>via </em>their <a href="https://discord.gg/8kqACuC">discord chat</a>.</p><h3>The race track</h3><p>To build the race track I took the <em>Town02 </em>map, deleted all props (as suggested <a href="https://carla.readthedocs.io/en/stable/map_customization/">here</a>), and thus prepared the scene for the race track.</p><p>The track is composed of RoadMesh elements that look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Eo_f-L_q3UGLTOVdffcd5w.png" /></figure><p>But to be able to create a smooth track I had to incorporate this element into a blueprint script called Track Generator. The race track is then created like this:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FMM4IrPoBtXs%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DMM4IrPoBtXs&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FMM4IrPoBtXs%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/fe8057ae09c4f7b8ab3a45d221bcb194/href">https://medium.com/media/fe8057ae09c4f7b8ab3a45d221bcb194/href</a></iframe><p>The race track on which I’ve actually rode looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P-DXKoFjt_vgg_oJJ13dqA.png" /></figure><p>I wanted to have sections of: tight corners, standard corners, and a couple of long straights. But, full disclosure, I have no idea what makes a track better or worse. For my purposes, this race track was perfect.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*W8CunTbB00ksIgYy8wCa2A.png" /></figure><h3>An interlude: a tour of the project</h3><p>I’ve forked the <a href="https://github.com/carla-simulator/carla">carla-simulator</a> repository, branched off from the stable version (release 0.8.2) into a branch racetrack and created a directory carla/PythonClient/racetrack where I keep all the necessary scripts for reproducing this work.</p><p>The two most important scripts are: client_controller.py and train_on_depth.py, where the first one is used for interfacing with CARLA, for controlling the car and for gathering data, whereas the second is used for training neural networks on the collected data.</p><p>Before running the client_controller.py script, remember to first initialize CARLA. On my system it requires the following:</p><pre>cd ~/CARLA<br>./CarlaUE4.sh /Game/Maps/Racetrack01 -windowed -carla-server</pre><p>After which, to control the car using a proportion-derivative (PD) controller (more details below), I call:</p><pre>python3 client_controller.py \<br>   --speed 45 \<br>   --controlled_name pd</pre><p>And the result should look more or less like this:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F535deGWNOT8%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D535deGWNOT8&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F535deGWNOT8%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/360328a69af51700db966de154667630/href">https://medium.com/media/360328a69af51700db966de154667630/href</a></iframe><p>We’ll get to the train_on_depth.py script in a minute.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*W8CunTbB00ksIgYy8wCa2A.png" /></figure><h3>The simplest possible controller</h3><p>Based on the experiences gathered from the <a href="https://eu.udacity.com/course/self-driving-car-engineer-nanodegree--nd013">Udacity course</a>, I knew one thing: I didn’t want to control the car myself. It would require multiple data collection sessions, would quickly become tedious and time consuming, and I would inevitably make mistakes that would require editing.</p><p>To reach my goal (training a neural network capable of going around the track), I started with the simplest controller that would collect the data for me. And it doesn’t get much simpler than the <a href="https://en.wikipedia.org/wiki/PID_controller">PD controller</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z50KudCCtrHb0OSuKp7p6A.png" /></figure><p>in which the steering angle at a given time <em>t </em>depends on the <em>Cross-Track Error </em>(CTE), and the time derivative of the CTE. The CTE is the distance between the center of mass of the car, and the desired trajectory. Also, the controller needs to be calibrated, i.e. it’s up to us to choose the coefficients <em>kp</em> and <em>kd</em> such that the car drives (relatively) smoothly. (The fact that Medium doesn’t have support for LaTeX is a pain to say the least.)</p><p>Admittedly, this is not the <em>simplest possible </em>controller because, as you might have thought, one might try a “P only” controller that ignores the second component. And yes, this is an even simpler controller but the <em>d</em>CTE/<em>dt </em>component plays an important role of stabilizing the output. Without it, the controller quickly becomes erratic:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FqOzlnpuq7Ss%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqOzlnpuq7Ss&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FqOzlnpuq7Ss%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a575e76b0771a23b6c2571b241daa5d0/href">https://medium.com/media/a575e76b0771a23b6c2571b241daa5d0/href</a></iframe><p>so it wouldn’t get the car around the track.</p><h4>Throttle</h4><p>The steering angle is not the only actuator we have at our disposal, there is also throttle. (And a handbrake, but we’re not going to touch it… yet.) But what we’re actually interested in is not throttle, but the thing it influences — velocity.</p><p>In the first iteration of this project I didn’t really care for velocity, I just wanted the car to go forward; not too fast, not too slow. To that end, I used a simple clip_throttle function that keeps the throttle within reasonable bounds, and tries to keep the speed close to the target_speed:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b75116b4fc250f1ee7e2961825962449/href">https://medium.com/media/b75116b4fc250f1ee7e2961825962449/href</a></iframe><p>Notice that this results in a wide range of throttle values, which in turn results in high variance of the velocity. But that’s OK; my reasoning was that this “augments” the data set and yields a more generalized model.</p><p>Technically, you could take the time and build a separate PD controller for the throttle so that a target speed is kept fairly constant. But this would require its own (<em>kp, kd</em>) parameters, making the model slightly more complicated.</p><p>But more importantly, in the second blog post we’ll use a controller that controls both actuators, steering angle and throttle, simultaneously. This approach is called Model Predictive Control (MPC), and is admittedly more complicated, but it provides a trajectory that is much more efficient. And if this didn’t whet your appetite for the second blog post, here’s a video illustrating how an MPC-controlled car drives around the track:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F6c2Eh1URvaQ%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D6c2Eh1URvaQ&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F6c2Eh1URvaQ%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/eec23f3ce487b103c1e1934ad3f908f5/href">https://medium.com/media/eec23f3ce487b103c1e1934ad3f908f5/href</a></iframe><p>Right away you can notice that the frame rate is much lower. This is because MPC requires some heavy optimization to be done on the fly, and my implementation of this controller is written entirely in Python.</p><h3>Building a neural network</h3><p>Partly inspired by <a href="https://bair.berkeley.edu/blog/2018/10/23/depth-sensing/">this</a> blog post and partly dissatisfied with the performance of a neural network model trained on pure RGB input (done as part of the <a href="https://eu.udacity.com/course/self-driving-car-engineer-nanodegree--nd013">Udacity Self-Driving Car Nanodegree</a>), I’ve decided to train a model on the depth map data that <a href="https://carla.readthedocs.io/en/stable/cameras_and_sensors/#camera-depth-map">can be easily collected in CARLA</a>. This is how the world looks like when seen through the depth map lens:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fh9T_gxbf67U%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dh9T_gxbf67U&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fh9T_gxbf67U%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/cdbd9151d2b2e4d550d5a624d7f4bd2c/href">https://medium.com/media/cdbd9151d2b2e4d550d5a624d7f4bd2c/href</a></iframe><p>Admittedly, it’s pretty pixelated and a bit unclear. But note that this representation carries 3D information that is essential for driving the car along the track.</p><p>I used keras for training the neural network, and used a fixed, singular architecture. I prefer raw code instead of fancy visualization tools, so here’s the definition of the model:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ca7ba9df273280d4a4a1835981a42d45/href">https://medium.com/media/ca7ba9df273280d4a4a1835981a42d45/href</a></iframe><p>I didn’t spend much time tweaking the architecture or the optimization parameters. I chose “reasonable” hyperparameters, and instead focused on modifying the input and adding auxiliary losses. But more on that in the second blog post.</p><p>The model had one job: predict the steering angle for a given depth map frame. It didn’t make much sense to predict throttle because it was independent of what was in front of the car (at least for our PD controller; this will change in the second blog post). So the throttle was also modulated using the clip_throttle function.</p><p>To train the model, I ran:</p><pre>python3 train_on_depth.py -c pd</pre><p>I had about 90k frames and the net took about 2h to train, but that’s because: 1) the model was over-the-top-complex, 2) I didn’t shrink the input image, and 3) the default number of epochs was 50. However, the MSE on the test set quickly became flat:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*yKSU78ByociOZ5FnlzcSKA.png" /></figure><p>And in reality, starting with “Epoch 2” the scatter plot of predicted vs actual steering angles looks pretty much constant:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*S3MwjdVwycwPWctG6FD05g.gif" /></figure><p>This is how the model with the lowest MSE on test set managed to drive around the track:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FmayKUBswH1s%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DmayKUBswH1s&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FmayKUBswH1s%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/55a250e973bdc4832b50a093ff9c28d4/href">https://medium.com/media/55a250e973bdc4832b50a093ff9c28d4/href</a></iframe><p>I think the coolest thing is that the neural network model seems to stabilize the oscillations better than the PD controller.</p><h3>Problems</h3><p>The biggest problem I haven’t yet solved is model selection. The MSE was insufficient, because it wasn’t always the case that the model with the lowest MSE on the validation set was the best. One way of dealing with this is to validate the model automatically in CARLA, without display. But I would like to have a more general approach, one that I could apply in the real world.</p><p>Also, I’ve noticed that models with the lowest MSE failed to steer the car out of tight turns. That is, high (but rare) values of the steering angle were never produced by such models in the simulator (even if on the test set the model seemed capable of yielding those steering angles). So I’m inclined to evaluate the performance of the model in two types of scenarios: normal, and extreme situations. But that’s for the future.</p><h3>Introduction to the CARLA simulator: training a neural network to control a car (Part 2)</h3><p>Be sure to check out the <a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-2-6a71115f2940">second part</a> of this series. It has more content (an euphemism for: “it’s longer”), and takes a look at different sources of data, created by different controllers, including a game pad!</p><h3>Training a neural network for driving an autonomous RC car</h3><p>There’s also an “unofficial” <a href="https://medium.com/asap-report/training-a-neural-network-for-driving-an-autonomous-rc-car-3906db91f3e">Part 3</a> which utilizes the exact same machine learning pipeline used for this project, but the neural net is steering an <em>actual </em>mobile robot and the racetrack is a path around a pond that’s next to my house.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*W8CunTbB00ksIgYy8wCa2A.png" /></figure><h3><strong>References</strong></h3><p><strong>Unreal Engine 4: </strong><a href="https://www.udemy.com/unrealcourse/">this</a> course on Udemy is a great intro but only if you’re into C++. Also, it’s <a href="https://carla.readthedocs.io/en/stable/CONTRIBUTING/#where-can-i-learn-more-about-unreal-engine">recommended</a> by the creators of CARLA.</p><p><strong>Race track:</strong> For building the race track I followed this tutorial: <a href="https://www.youtube.com/watch?v=wR0fH6O9jD8">youtube link</a>. Also, I found <a href="https://docs.unrealengine.com/en-us/Engine/BlueprintSplines/Reference/SplineEditorTool">this</a> part from the UE4 documentation valuable. Actually, the whole UE4 is documented very well, <a href="https://docs.unrealengine.com/en-us/Engine/Physics/Vehicles/VehcileContentCreation">here’s</a> one of the gems I intend to explore.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*FryiKUSNM4m7IFMw_9iG_w.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e1c2c9a056a5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/introduction-to-the-carla-simulator-training-a-neural-network-to-control-a-car-part-1-e1c2c9a056a5">Introduction to the CARLA simulator: training a neural network to control a car (Part 1)</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Experiences and thoughts from the 3rd F1/10th competition]]></title>
            <link>https://medium.com/asap-report/experiences-and-thoughts-from-the-3rd-f1-10th-competition-2c46508e2719?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/2c46508e2719</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[racing]]></category>
            <category><![CDATA[self-driving-cars]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Sun, 28 Oct 2018 13:01:05 GMT</pubDate>
            <atom:updated>2019-02-11T20:10:07.230Z</atom:updated>
            <content:encoded><![CDATA[<p>Our team won the F1/10th competition in Torino! Now that the celebrations are over, it’s time to analyze and draw conclusions from the event to prepare ourselves for the upcoming competition in 2019.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3G5y0Y8D24vniZhkxUj1oQ.png" /><figcaption>This is Dzik</figcaption></figure><h3>Introduction</h3><p>The <a href="http://f1tenth.org/">F1/10th</a> is a racing competition for autonomous, electric car models. The organizers (University of Pennsylvania) encourage the participants to develop the fastest (yet, reliable) algorithm, capable of beating a track. The hardware parameters are capped by the rules, and most of the work needs to be devoted to software.</p><p>Here’s a short summary video from the event:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FVlE2Wb_XhoQ%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVlE2Wb_XhoQ&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FVlE2Wb_XhoQ%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ba31d111f71e15653b7f9141e79a34fa/href">https://medium.com/media/ba31d111f71e15653b7f9141e79a34fa/href</a></iframe><p>The winner of the 3rd F1/10th challenge was chosen based on the number of laps made by the model during two timing sessions. The first sessions lasted 5 minutes, while the second one was shorted and took 3 minutes. If the car needed any kind of assistance, or damaged the track during a lap, the team was required to pause the lap, go back to a previous checkpoint, and continue the lap from there. If the number of laps of two teams were the same, the team with the fastest lap was considered better.</p><h4>The track</h4><p>This edition’s track was quite challenging, with multiple turns that proved difficult to all of the teams:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/487/1*Vs69gMMnYa1MOw2ToUNnjw.jpeg" /><figcaption>The track of the 3rd F1/10th competition in Torino. The red line marks the start/finish-line, and the numbered corners are referenced in the main text. The map was generated from LIDAR data by Karol Majek.</figcaption></figure><p>The track started with a simple left turn, followed by two consecutive U-turns. The second U-turn <strong>(1)</strong> was especially difficult — the cars either tried to go back on this turn, or cut the corner too aggressively.</p><p>After beating turn <strong>(1) </strong>there was a long straight that ended with a tricky turn <strong>(2)</strong>. In the beginning, our car experienced a bit of oversteer, and even turned around. Eventually, we limited the speed of the car in the preceding straight, and narrowed the LIDAR’s scan so that the car wouldn’t be lured to go back.</p><p>Then, there’s an easy, slow left turn, followed by the longest straight on the entire track, ending in a left turn <strong>(3)</strong>. On the one hand, we wanted the car to go as fast as possible on this straight, but on the other hand not too fast, so as to fit in the <strong>(3)</strong>rd corner.</p><p>Also, the surface on the entire track was somewhat slippery which also added to the difficulty of the race.</p><p>Here’s a video from the first round of the competition (our run starts at 43:00):</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FsHpdfObk-mw%3Fstart%3D2580%26feature%3Doembed%26start%3D2580&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DsHpdfObk-mw&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FsHpdfObk-mw%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/3ea8b7586b4b77353c6e8471255c435d/href">https://medium.com/media/3ea8b7586b4b77353c6e8471255c435d/href</a></iframe><p>We finished 16 laps, and the fastest took 18.29 s.</p><h4>The simulator</h4><p>The organizers shared a <a href="https://github.com/mlab-upenn/f1_10_sim">Gazebo simulator</a> for prototyping and testing out models. And although there’s a huge gap between how the car behaves in the simulator vs. how it behaves on the actual track, the simulator proved to be a valuable tool for trying out new ideas.</p><h3>The team and the car</h3><p>First off: we had very limited time to prepare for the competition. Keep that in mind, and if you’ll run into question like: “Why did they use RaspberryPi instead of the Jetson?” the likely answer is: “We were in a hurry and had to cut corners (pun intended).”</p><p>The team members were:<br>- <a href="https://pl.linkedin.com/in/lukaszsztyber">Łukasz Sztyber</a><br>- <a href="https://www.linkedin.com/in/karolmajek/">Karol Majek</a><br>- and <a href="https://www.linkedin.com/in/maciej-dziubinski/">myself</a><br>Three guys passionate about self-driving cars. And that’s all you need to take part in the competition. OK, that’s not all you need — the car is pretty important too.</p><p>We started off with the <a href="https://erlerobotics.com/blog/product/erle-rover/">Erle Rover</a> platform, which came with a RaspberryPi3 as the computational unit, and a very simple RGB camera. We first tried to work with the camera but that led to nowhere (even a simple deep learning line-follower model was challenging to train). For one thing, the inference time was too long. But more fundamentally, it became clear that a deep learning approach would not generalize to new tracks, and in particular: the track we would need to beat at the competition. (Which is not to say that a machine learning approach is a dead end — it’s not. But I think it needs to be used for learning “things” that generalize to other tracks.)</p><p>Then Łukasz bought the <a href="https://acroname.com/products/HOKUYO-UST-10LX-LASER">recommended LIDAR,</a> and even the simplest approaches based on a LIDAR seemed promising. It became clear that it’s possible to build a model without <a href="https://en.wikipedia.org/wiki/Simultaneous_localization_and_mapping">SLAM</a>, without path planning and control. This was crucial since at that point we had very little time remaining before the competition started, and we knew that only simple solutions would be achievable.</p><p>Once we became more serious about the competition, Łukasz invested in a platform which he dubbed Dzik (pronounced “j ee k”). We used the RaspberryPi3 from the Erle Rover as the computational unit. The organizers recommended NVidia Jetson TX-1, or TX-2, which was faster, but we wouldn’t have time to set it up, configure, and adapt to the rest of our hardware. We stuck to RaspberryPi3, and allocated what time we had left to improving the solution.</p><h3><strong>The solution</strong></h3><p>Our solution was a variant of the <a href="https://doi.org/10.1016/j.robot.2012.05.021">“follow-the-gap” approach</a> for steering, combined with a set of conditions determining the speed of the car. For example, if the optimal direction (the “gap”) is straight ahead, and the distance to the obstacle in that direction is beyond a certain distance (which was one of many parameters of the method), then: go fast. How fast exactly was also controlled by a parameter, and needed adjusting.</p><p>Also, it wasn’t actually speed that was controlled, but rather, a Pulse-Width Modulation (PWM) signal passed to the Electronic Speed Controller (ESC), which in turn ran the engine at a particular RPM corresponding to that PWM value. Although we had odometry and tested out a <a href="https://en.wikipedia.org/wiki/PID_controller">PID controller</a> for speed, it turned out that the ESC was able to reach a stable RPM for a given PWM pretty quickly, so there wasn’t much gain from a PID for speed. That’s all pretty technical, but the gist of this whole paragraph is: we had three PWM values, and we <em>assumed</em> that they correspond to three speeds, and that the engine can quickly switch between these speeds.</p><p>In addition to the rules regarding speed, there was a set of rules for stopping. It’s impossible to count all the instances during practice in which we were certain that the LIDAR took the final blow, and that we won’t be able to run in the competition. Therefore, our first line of defense against ‘the blackest scenario’ was a set of conditions in which Dzik just stopped.</p><p>Karol says that effectively the rules for going slow/medium/fast form a decision tree. We didn’t train its parameters using observable data, but rather fit it to our intuitions. It’s a bit of a stretch, but you could say we adapted reinforcement learning with an experience-driven optimization algorithm :)</p><p>But more seriously, we could have used supervised machine learning, and: record several laps (including minor mishaps) by controlling the car ourselves, fit a machine learning model, validate it on another set of recorded laps (paying close attention to how the model behaves on mishaps), and expect to get a better solution then those hard-coded conditions we used. However, we didn’t go this path, and it’s hard to tell how many problems we would have encountered. In particular: how many laps would be needed? How much time would we need to adapt to a new track? And perhaps the most important unknown: how hard would it be to debug such a model? Remember that our <em>final</em> validation is on the actual track, and if we observe that something goes wrong, it’s very important that we know how to improve upon it. But here, what more can we do other than “get more data”? This is a problem in and of itself. And that’s just speed control, I haven’t even mentioned steering.</p><p>So in other words: using an adaptive model seems cool, but it has its drawbacks. Still, it doesn’t mean we won’t try it.</p><h4>Limitations</h4><p>Fundamentally, even if our method found the optimal steering angle <em>locally, </em>it wouldn’t be able (in general) to yield the optimal route <em>globally.</em></p><p>To follow the optimal route, one needs to solve the problems of: localization, planning, and control. Such an approach is admittedly <strong>much</strong> more complicated, but it holds a promise of reaching a systematic solution, one that can become increasingly better, as the solutions to the individual sub-problems improve.</p><p>But a more important reason for not using the follow-the-gap approach again (at least not in its existing form) is actually more simple. Namely: we would like to try something else.</p><h4>Strengths</h4><p>The follow-the-gap approach is fast. The LIDAR (with nominal frequency of 40Hz), produces a new scan every 25ms. The C++ implementation was able to finish its computations in approximately 3ms (on average). That’s quite important during a race, where each stalled millisecond makes the scan increasingly obsolete.</p><p>But there’s also another strength: this method is fairly easy to adapt to a new track. The parameters controlling its behavior are intuitive (once you get to know what they actually control), and finding their optimal values for a new track is a matter of several test runs. (Actually, the track in Torino was pretty challenging, so it took 20–30 runs, but still.)</p><p>There’s one more strength, but we haven’t really made use of it during this competition. And that is: the follow-the-gap approach can automatically handle moving objects on the road, and avoid them or even overtake them (if it’s an opponent). It may not seem like that big of a deal, but the alternative, systematic approach will probably need more attention in this particular area. And because the time needed to make a decision would inevitably be longer, the algorithm would need to anticipate future positions of the moving objects. So it might get interesting.</p><p>The last year’s winners (from Czech Technical University) also used a version of the follow-the-gap approach. And that makes you wonder: perhaps this method is indeed superior to the more systematic ones? Maybe the drawback of following the optimal route is outweighed by the benefit of making locally optimal decisions very quickly? I think the systematic solution is difficult to get right, and that’s one of the main <em>relative</em> advantages of the follow-the-gap approach. But how long will it hold — hard to say.</p><h3>Plans for the next competition</h3><p>…are top-secret and I can’t share them here.</p><p>But follow the f1tenth.org forum, and meet us at the next competition, and we will gladly share them with you. Right after the race, that is ;)</p><h3>References</h3><ul><li><a href="http://f1tenth.org/">f1tenth.org</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*FryiKUSNM4m7IFMw_9iG_w.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2c46508e2719" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/experiences-and-thoughts-from-the-3rd-f1-10th-competition-2c46508e2719">Experiences and thoughts from the 3rd F1/10th competition</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[FaceNet with memory vs. synthetic faces]]></title>
            <link>https://medium.com/asap-report/facenet-with-memory-vs-synthetic-faces-8e87a009e139?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/8e87a009e139</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[facial-recognition]]></category>
            <category><![CDATA[tensorflow]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Wed, 23 May 2018 07:11:49 GMT</pubDate>
            <atom:updated>2019-02-11T20:10:40.800Z</atom:updated>
            <content:encoded><![CDATA[<p>We test FaceNet’s performance on a set of unconventional faces and show how to stitch it with a memory module.</p><p>Written by <a href="https://medium.com/@ponadto">Maciej Dziubiński</a> and <a href="https://medium.com/@marta.sd">Marta Stępniewska-Dziubińska</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ARMyxbRxJldVekoNa03y5A.jpeg" /></figure><p>Disclaimer: this blog post and the whole project was done “for fun” — it doesn’t really serve any purpose other than sharing something we think is a cool project. From the Reader’s perspective, there are two main merits coming from this post:<br>1. Seeing how FaceNet can be used to recognize people (or, rather, animated characters) in a one-shot manner;<br>2. Learning how to stitch FaceNet with another “building block”, namely: with the memory module proposed by <a href="https://arxiv.org/abs/1703.03129">Kaiser et al</a>.</p><p>Additionally, if you haven’t had much experience with TensorFlow, the <a href="https://github.com/asap-report/facenet_with_memory">accompanying repository</a> and its <a href="https://github.com/asap-report/facenet_with_memory/blob/master/results.ipynb">Jupyter Notebook</a> may also be of value to you.</p><p>We’ve tried to keep this blog post accessible to the casual Reader, which means that we’re not going through the implementation of the models, the memory module, or any other (overly) technical details. We hoped to capture the Reader’s attention, and if that succeeded, provide him with all implementational details needed to re-create the experiments. So keep that in mind, and check out the implementation if you find this project interesting.</p><h3>Introduction</h3><p>OK, so FaceNet is incredibly good at face recognition of real human beings. That’s great and all, but is it capable of recognizing faces across domains: CGI, painted, drawn, etc.? We asked ourselves this critical question, and <strong>the short answer is</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*ndOSe5hdezUnqvDbXrPz6g.gif" /><figcaption>Source: <a href="http://researchinprogress.tumblr.com/">http://researchinprogress.tumblr.com/</a></figcaption></figure><p><strong>The set-up<br></strong>Imagine that you’re organizing a venue in a VR world (similar to that in <a href="https://www.imdb.com/title/tt1677720/"><em>“Ready Player One”</em></a> or in another book adaptation: <a href="https://www.imdb.com/title/tt1821641/"><em>“The Congress”</em></a>) where each participant chooses an avatar. Let’s say that, for whatever reason, you would like to register, and then recognize the participants based on their animated faces using some standard face recognition model.</p><p>This might be tricky since most face recognition models are trained on actual (human) faces, so even if the task of recognizing avatars is easy for us (humans), it might be hard for a model. The source of the problem is the difference between the <em>domain</em> on which the model was trained on, and the new, animated <em>domain </em>for which we would like to use the model. For more on this topic we refer the Reader to <a href="http://ruder.io/transfer-learning/">this</a> blog post, but for the purpose of this project it’s only important to note that: <strong>it’s hard to know beforehand how a face recognition model will perform on animated faces.</strong></p><p>But as it turned out, we didn’t have to do any domain adaptation —FaceNet performs pretty well without any additional work.</p><h4>FaceNet</h4><p>is our face recognition model of choice. As mentioned, it performs very well on real faces (reaching an accuracy of 0.99650 on the <a href="http://vis-www.cs.umass.edu/lfw/">Labeled Faces in the Wild</a> dataset).</p><p>FaceNet builds on the <a href="http://arxiv.org/abs/1602.07261">Inception ResNet v1</a> architecture and was trained on the <a href="http://www.cbsr.ia.ac.cn/english/CASIA-WebFace-Database.html"><strong>CASIA-WebFace</strong></a> and <a href="https://www.robots.ox.ac.uk/%7Evgg/data/vgg_face2/"><strong>VGGFace2</strong></a><strong> </strong>datasets. It’s worth noting that FaceNet’s weights are optimized using the <a href="https://www.coursera.org/learn/convolutional-neural-networks/lecture/HuUtN/triplet-loss">triplet loss function</a>, so that it learns to embed facial images into a 128-dimensional sphere. Images whose embeddings are close in this space are expected to correspond to similar faces. If you want to learn more, we refer you to the <a href="https://arxiv.org/abs/1503.03832">paper</a> and if you really<em>, really</em> want to learn more, check out <a href="https://github.com/davidsandberg/facenet">its TensorFlow implementation</a>.</p><p>We won’t go into details concerning FaceNet’s architecture, but for the purpose of the discussion below, it’s important to highlight one particular nuance of the Inception architecture. Namely, it has a train phase (True or False), which controls whether Dropout and BatchNorm are turned ON (True) or OFF (False). Dropout is not that big of a deal, but for those of you who haven’t yet fallen into the pitfall of re-using a model with BatchNorm, here’s a <a href="https://medium.com/luminovo/a-refresher-on-batch-re-normalization-5e0a1e902960">good reference</a>. We’ll see that in our case it’s better to turn the train phase OFF.</p><h4>The memory module</h4><p>We’ll use the memory module proposed in the <a href="https://arxiv.org/abs/1703.03129"><em>Learning to Remember Rare Events</em></a> paper by Kaiser et al. Roughly, the memory is composed of three elements: 1) a matrix with rows corresponding to embeddings of observations, 2) a vector storing the labels of the observations stored in this matrix, and 3) a vector of ages of those observations.</p><p>Point 1) is not entirely true: a single row may also result from combining two (or more) similar observations, which is done auto-magically by the memory module. This will be relevant later on, when we’ll argue that one of the merits of the memory module is that it requires storing fewer embedding, and thus: less memory.</p><h3>Methods and Data</h3><h4>Data</h4><p>Our dataset is tiny, comprising 12 characters, each with 8 distinct images (drawn, painted, etc.), which yields 96 images. Here they are:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wZbNRsIIoth71rR60bFGQw.png" /><figcaption>6 pairs = 12 characters, 8 images each, 96 images total.</figcaption></figure><p>You might have noticed that the 12 characters are grouped into 6 pairs of similar faces, i.e.:<br>* Geralt vs. Vesemir (two witchers, as they were “portrayed” in <a href="https://en.wikipedia.org/wiki/The_Witcher_3:_Wild_Hunt"><em>The</em> <em>Witcher 3</em></a>);<br>* <a href="https://en.wikipedia.org/wiki/Jacek_Malczewski">Malczewski</a> vs. <a href="https://en.wikipedia.org/wiki/Stanis%C5%82aw_Ignacy_Witkiewicz">Witkacy</a> (two influential Polish artists);<br>* Heroin and hero of the <a href="https://en.wikipedia.org/wiki/Avatar_(2009_film)"><em>Avatar</em> movie</a> (we didn’t really remember their names, so we named them avatar_female and avatar_male);<br>* Gollum vs. Smeagol (from the movie adaptation of the <a href="https://en.wikipedia.org/wiki/The_Lord_of_the_Rings_(film_series)"><em>Lord of the Rings</em></a>);<br>* Durotan vs. Hulk (from the <a href="https://en.wikipedia.org/wiki/Warcraft_(film)"><em>Warcraft </em>movie</a>, and the <a href="https://en.wikipedia.org/wiki/Marvel_Cinematic_Universe">Marvel Cinematic Universe</a>);<br>* Thade vs. Cesar (from two versions of the <em>Planet of the Apes; </em>Thade from <a href="https://en.wikipedia.org/wiki/Planet_of_the_Apes_(2001_film)">Tim Burton’s adaptation</a> and Cesar from the <a href="https://en.wikipedia.org/wiki/Planet_of_the_Apes#Reboot_film_series">reboot series</a>).</p><p>Note, that Gollum and Smeagol are actually alter egos of the same character so we expected the model to struggle with these two. But on the other hand, humans are pretty much able to discern between the two just by looking at their expressions, so why not put FaceNet to test?</p><p>Another note: Witkacy was represented by 4 painted/drawn pictures and 4 photographies. We’ve expected this to be a problem, but as we’ll see in a minute —FaceNet was resilient to this obstacle.</p><h4>FaceNet</h4><p>We used the <a href="https://drive.google.com/file/d/0B5MzpY9kBtDVZ2RpVDYwWmxoSUk">20170512–110547</a>model. We have also tested out the newer models (20180402–114759 and 20180408–102900) but got better results with the older one.</p><p>FaceNet was used to produce embeddings for the 96 images, and if we use the dot product between these embeddings, we can summarize the similarity between the images with a matrix (row and column indexes are grouped by characters):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/766/1*v0GXQiMegK-9rdyCW-NRzw.png" /></figure><p>Because the embeddings are normalized, the dot product is equivalent to the cosine similarity. The dark blocks along the diagonal represent high similarities between faces coming from the same character. But notice also that Gollum and Smeagol together yield an almost homogenous, dark block, which is in line with what we anticipated: these two will pose a challenge to the model.</p><h4>Memory</h4><p>In an upcoming blog post we’ll go through <a href="https://github.com/tensorflow/models/tree/master/research/learning_to_remember_rare_events">the implementation of the memory module,</a> but here we’ll only mention several of its most important features. First: there is a “bookkeeping” aspect of the memory which is not driven by gradient propagation, but rather in a “insert-update-delete” fashion. Second: the gradient of the loss function is propagated to the model providing the embedding (FaceNet, in our case). And lastly: the prediction is done by finding in the memory matrix the best matching observation (in a 1-nearest-neighbor fashion, using the cosine distance), and returning its corresponding label.</p><h4><em>K</em>-shot learning and evaluation</h4><p>We iteratively trained and evaluated the model. In the first iteration, the model was trained on 12 images, one face per character. Next, the model was evaluated on the remainder of the images. On the second iteration, the model was fed another batch of 12 images, then evaluated on the rest, and so on, and so forth. Actually, there’s also a 0-th iteration, where the model starts off completely blind, without seeing any of the characters, which, as expected, results in an accuracy of about 1/12 = 8.33%.</p><h4>Two models</h4><p>We will compare two models, both based on the embeddings produced by FaceNet. The first one will utilize the memory module and will be called “FaceNet + memory”. The second one will be simpler: it will store <em>all </em>embeddings and their corresponding labels, and produce a prediction in a 1-nearest-neighbor fashion. We will call this the “FaceNet alone” model.</p><p>The “FaceNet alone” model may at first seem remarkably similar to the “FaceNet + memory” model. But it differs in two respects: 1) this model won’t modify the embedding model (i.e. FaceNet), and 2) because it will store <em>all </em>previous embeddings, it will require more, well, memory. The memory module uses a fixed-sized matrix to store the embeddings, and our default number of rows was <strong>32 </strong>(less than the number of images). Note that, because the “FaceNet + memory” had a cap on the (RAM) memory, its task was a bit harder. Let’s see how it influenced the performance.</p><h3>Results</h3><h4>K-shot accuracies</h4><p>Finally, let’s see the k-shot accuracies for the “FaceNet alone” and the “FaceNet + memory” models:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*AQzVg2yVnu7ZpDozaVO_mw.png" /><figcaption><em>K</em>-shot accuracies for “FaceNet alone” and “FaceNet + memory” (with train phase ON).</figcaption></figure><p>That’s what we initially saw. But we didn’t expect the memory module to be <em>that </em>much worse, so we asked ourselves: what are we doing wrong?</p><p>And it’s not that we had a bug in the code, but rather that we forgot to pay attention when applying transfer learning with a pre-trained model that uses BatchNorm. Once we turned the train phase OFF, the results were as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*IgeE5PEm074245FsPjRNHQ.png" /><figcaption>K-shot accuracies for “FaceNet alone” and “FaceNet + memory” (with train phase OFF).</figcaption></figure><p>Consequently, FaceNet’s weights are updated <em>without</em> the correction for internal covariate shift (partially amended by BatchNorm), and <em>without</em> the ensembling provided by Dropout. Is this OK? Can it be done better? We are open to suggestions so if you have one — please, leave a comment.</p><p>To see what kind of mistakes the models made, we compiled two confusion matrices:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*09YcofL859wW618aklXsFw.gif" /><figcaption>Evolution of the confusion matrices for the two models (rows represent labels, columns represent predictions).</figcaption></figure><p>The rows of the confusion matrix represent actual labels, and the columns denote predictions. Bu default, the 0-shot prediction of the “FaceNet alone” model is a uniform random choice between the labels of all characters. On the other hand, the 0-shot prediction given by “FaceNet + memory” is “geralt” (class 0) for every image, which corresponds to a black column in the confusion matrix.</p><p>For details regarding the implementation, the default values of hyper-parameters, and everything else, we refer the Reader to the accompanying <a href="https://github.com/asap-report/facenet_with_memory">repo</a>.</p><h3>Discussion</h3><p>We will discuss the “FaceNet alone” solution first, and then move on to “FaceNet + memory” .</p><p><strong>FaceNet alone<br></strong>This approach is doing a pretty good job, with 83% accuracy right off the bat, after seeing each character just once. It even reaches 100% accuracy, which means that it was able to distinguish (among others) between Gollum and Smeagol.</p><p>From the evolution of the confusion matrix we see that the model struggles with discerning between Gollum and Smeagol, but has also problems with another pair: Hulk and Durotan. Still, that’s pretty much it, the rest of the character pairs are no match for this simple 1-nearest-neighbor approach based on FaceNet’s embeddings.</p><p><strong>FaceNet + memory<br></strong>Obviously, we would really like to sell this method like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/446/1*JX8n9ZO7wax2FR9Z07XxfA.gif" /><figcaption>Source: <a href="http://researchinprogress.tumblr.com/">http://researchinprogress.tumblr.com/</a></figcaption></figure><p>but the Reader will benefit more from a self-critical approach.</p><p>The accuracy is lower than that of “FaceNet alone” but bear in mind that there’s a bit of variability in the results. If we were really fussy, we would repeat the experiment many times, bootstrapping (or rather: shuffling) the images for each character. Also, we might have spent more time tweaking the hyper-parameters. But we left that as an exercise for the Reader.</p><p>The evolution of the confusion matrix indicates that, again, Gollum and Smeagol are a problem, but more surprisingly the model mistakes Durotan (an orc, mind you) for Vesemir (a witcher). But, conversely to “FaceNet alone”, starting with the 3rd shot, there are no misclassification between Durotan and Hulk. This is weird because from what we saw in the similarity matrix, Durotan has relatively low similarity with Vesemir (at least in comparison with Hulk). We therefore suspect that this is an artifact of this particular ordering of the input images.</p><p>Now, what can be said for sure is that “FaceNet + memory” required less memory than the “FaceNet alone” model. Roughly three times less memory. But if each character occurred more frequently, the gain would have been even higher. Going a bit further in that direction: the “FaceNet + memory” is prepared for a life-long learning path, batteries included, whereas the “FaceNet alone” would require unlimited memory.</p><p>And as the learning path of “FaceNet + memory” progresses, the model adapts to the domain in which it’s being applied. Our dataset is too small to make use of this property, but it might come in handy in a more demanding environment.</p><p>There are, however, two limitations of the memory module that we wish to address in the future:<br>1. The prediction is made based only on the closest observation which might increases the variance of the model (in the sense of the <a href="https://en.wikipedia.org/wiki/Bias%E2%80%93variance_tradeoff">bias/variance tradeoff</a>);<br>2. During training, a row in the memory matrix may be updated by combining two embeddings: the one already in the memory and another, coming from a new, but similar observation. This “combining” boils down to adding the two embeddings. However, this way we loose information about the distributions of the embeddings.</p><h3>Conclusions</h3><p>We evaluated two approaches to k-shot face detection based on embeddings acquired with the FaceNet model. The first one was based on a memory module proposed by Kaiser et al., the second was a simple 1-nearest-neighbor approach.</p><p>Our dataset was composed of 96 facial images of 12 (mainly) CGI characters from movies, video games, and art — domains which FaceNet was <em>not</em> originally intended for.</p><p>Both models reach an accuracy of 100% after “seeing” each character 7 times, although we stressed that this result may be noisy. However, this proves that the embedding learned by FaceNet is robust and that it can be transfered across domains, e.g. for face recognition based off of portraits done by criminal sketch artists.</p><p>If you would like to learn more about the memory module, make sure to check out the <a href="https://arxiv.org/abs/1703.03129">paper</a> and the <a href="https://github.com/tensorflow/models/tree/master/research/learning_to_remember_rare_events">implementation</a>. We’re also working on a blog post in which we go through the implementation using the eager execution in TensorFlow, so stay tuned!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*FryiKUSNM4m7IFMw_9iG_w.png" /></figure><h3><strong>References</strong></h3><ul><li>Kaiser, Ł., Nachum, O., Roy A., Bengio S. (2017). <em>Learning to Remember Rare Events. </em><a href="https://arxiv.org/abs/1703.03129">https://arxiv.org/abs/1703.03129</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*FryiKUSNM4m7IFMw_9iG_w.png" /></figure><p>If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML &amp; AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8e87a009e139" width="1" height="1" alt=""><hr><p><a href="https://medium.com/asap-report/facenet-with-memory-vs-synthetic-faces-8e87a009e139">FaceNet with memory vs. synthetic faces</a> was originally published in <a href="https://medium.com/asap-report">Acta Schola Automata Polonica</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Supervised Models for User Verification — Part 2 of the Tutorial]]></title>
            <link>https://medium.com/daftcode-blog/building-supervised-models-for-user-verification-part-2-of-the-tutorial-1ba807029f74?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ba807029f74</guid>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Tue, 30 Jan 2018 10:52:42 GMT</pubDate>
            <atom:updated>2018-03-16T16:33:45.103Z</atom:updated>
            <content:encoded><![CDATA[<h4>Neural-network-based models + assessment on test set</h4><p>Written by <a href="https://medium.com/u/e51fd42b6648">Maciek Dziubiński</a> and <a href="https://medium.com/u/c2e894061ac2">Bartosz Topolski</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ea3G3Ey__5DO-5jWZO2Zgg.png" /><figcaption>Illustration by <a href="https://dribbble.com/magdalenatomczyk">Magdalena Tomczyk</a></figcaption></figure><blockquote>If you haven’t read the first part of the tutorial, you can check it out <a href="https://blog.daftcode.pl/building-supervised-models-for-user-verification-part-1-of-the-tutorial-7496d5d394b9"><strong>here</strong></a>.</blockquote><p><strong>tl;dr </strong>We continue the development of supervised models for user verification based on behavioral data. In this part, we’ll focus on neural-network-based models, and finalize this series by looking at performances of all four models on the test set.</p><p>This tutorial follows <a href="https://mybinder.org/v2/gh/nethone/keystroke-dynamics/master?filepath=workshop.ipynb">this notebook</a>, so it makes sense to open it now, hit “Cell -&gt; Run All”, and then go back and forth between this post and the notebook.</p><h4>2. Multi-Layer Perceptron</h4><p>The Multi-Layer Perceptron (MLP) is an example of a wide family of models called neural networks. It’s a simple example, but because of that, it’s worth taking a slightly deeper look into, before moving on to siamese neural networks.</p><p>MLP is a stack of subsequent layers of neurons (hence the name). The picture below depicts a small, three-layered network. MLP is a type of the so-called <em>feed-forward</em> architecture, i.e. each neuron takes as input activations of all neurons from the previous layer, processes it in a certain way (we’ll look into that in a moment) and is then used as input by the next layer.</p><p>So, what does a neuron “do” exactly? Let’s assume that our input data is a vector <strong><em>x</em></strong>. Each neuron is calculating a dot product of the input vector <strong><em>x</em></strong> and this neuron’s vector of weights <strong><em>w </em></strong>— that is, multiplying each input coordinate by a weight and summing it all up to a single number. Then, a value called bias is added to the dot product, and finally, the result is transformed by some non-linear function, <em>f</em>.</p><p>Since there are multiple neurons in each layer, we can represent the layer as a matrix, with columns corresponding to weight vectors of individual neurons. The biases are, on the other hand, represented as a vector, <strong><em>b</em></strong>. Those weights and biases are what makes a model provide (hopefully) valuable predictions, and finding them is the main goal of training a neural network model. Using linear algebra’s notation, we can represent our whole model in just a few simple equations seen next to the visualization of a network:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Aqbv0bc9NC4p0HiSJ5OZHA.png" /></figure><p>Thanks to the non-linearity of the activation functions, our model can estimate almost any mapping from inputs to output. That is, if the number of layers and neurons in them are sufficient. The only thing we have to do now is finding the optimal weights and biases for each layer (collectively called <em>parameters</em>).</p><p>As in any machine learning problem, we train the model by minimizing some loss function on the training set. We know that the loss function depends on predictions from our model, but using the equations mentioned earlier we can also see that predictions depend (more indirectly) on weights and biases.</p><p>Because we can define our loss as a function of the parameters, we can also differentiate it with respect to those variables — and that gives us information about how the loss functions change as we change the parameter values. We can then decrease the loss function iteratively, by making tiny updates to the parameters, proportional to the derivatives of the loss function with respect to those parameters.</p><p>However, there is much more to training neural networks, and machine learning in general, than just minimizing the loss function on the training set (although this alone can be a challenge). We would like the model to generalize well to new, unseen data. This is one of the practical differences between neural nets and the <a href="https://blog.daftcode.pl/building-supervised-models-for-user-verification-part-1-of-the-tutorial-7496d5d394b9">previously mentioned</a> XGBoost model — they’re not as good “out-of-the-box”, and typically require more effort to make them generalize well.</p><p>Another difference is that we need to standardize the input feature values. This is done using the RobustScalerForPandas (defined in code cell <strong><em>[20]</em></strong>), which is a wrapper that makes sure that the transformed frame is a pandas.DataFrame. This wrapper inherits from the sklearn.preprocessing.RobustScaler, and it’s interesting to check if there are scaling strategies leading to better results.</p><p>The MLP model is defined in code cell <strong><em>[21]</em></strong>; its architecture is specified by parameters passed at initialization, but the activation functions and the optimization procedure are hard-coded. Also, note that there’s a considerable class imbalance in our data set, so maybe it might be worth playing with the class_weight argument passed to the model.fit method?</p><p>But even without much tweaking of the hyperparameters, the MPL model performs pretty well — we get AUC scores of about 0.90, which is pretty similar to what we got using XGBoost.</p><h4>3. Simple siamese neural network</h4><p>Let’s move to code cell <strong><em>[24]</em></strong>, where we can see how the input data frame looks like when estimator.only_feature_diffs is False. Note, that the custom_cross_val_auc_score function actually calculates this frame internally, and we’re not going to use the DF_train_all_pairs variable for anything other than seeing how it looks like. Each column (besides the label column) either ends with an “_A”, or with a “_B”. These suffixes signify to which part of the input a given column belongs to in the siamese model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dNdkZJ8tspsel2M_nFoXrQ.png" /></figure><p><strong>Input A</strong> and <strong>Input B</strong> correspond to two keystroke dynamics observations. Then, both inputs are processed by a three-layered neural network, whose activations on the last hidden layer (<strong>Hidden layer 3</strong>) are then used to calculate their absolute difference (<strong>L1 merge</strong> layer for short), followed by a single hidden layer (<strong>One more hidden layer</strong>), and finally leading to the <strong>Output</strong> layer which returns a value between 0 and 1. That’s how we get our estimate of the probability that the two sequences, described by <strong>Input A</strong> and <strong>Input B</strong>, come from a single user.</p><p>There are two important notes here. First note: the three-layered networks built on the Inputs are actually two “references” to <em>the same network</em>. That is, these two legs share the same weights — if we were to modify any part of the left leg, the right leg would change as well. Second note: the order of observations supplied as Inputs does not matter, because the <strong>L1 merge</strong> layer performs a symmetric operation. The symmetry refers to the arguments, i.e. any op such that <em>op(A, B) = op(B, A)</em> would also work. In fact, you can play with this <em>op</em>, and use, for example:</p><pre>def l2_diff(x): return K.pow(x[0] - x[1], 2)</pre><p>which corresponds to <em>op(A, B) = (A - B)²</em>, but be aware that the “-” and “( )²” operations are performed in a vectorized manner.</p><p>Note that this time, we set the only_feature_diffs attribute to False since we want the custom_cross_val_auc_score to use a data frame with actual feature vectors, and not an absolute difference between them.</p><p>On validation, the simple siamese model seems to perform OK (again, the AUC is ~0.90). But keep in mind, that we still need to measure the performance of the model on the test set.</p><h4>4. Siamese recurrent neural network</h4><p>We now move to code cell <strong><em>[30]</em></strong>, where we train another siamese model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CDytwkyw1RjXkkcKAKPdNg.png" /></figure><p>but with a twist: instead of a three-layered siamese legs, we use recurrent units, operating on sequences rather than on handcrafted features. We would like these recurrent units to <em>learn features</em> as valuable, or better, than aggregated statistics of dwell and flight times.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*2RLfZi7VR4VUqLrFUPd3hw.jpeg" /></figure><p>Obviously, we might get better results if we merged the activations from the GRU with an MLP working on our standard features (remember? the mean and std of the dwell times, etc.). Or even if we used dwell and flight times sequences themselves. But that’s not what we had in mind — we would really like the model to learn the features directly from the sequence, without any expert knowledge. It so happens that we <em>knew</em> that dwell and flight times are indeed valuable characteristics, but what about other types of behavioral data, e.g. mouse movements?<br>We know that’s utopian thinking, but we believe that some day we’ll be able to replace the skill of coming up with great features (typically pertinent to one, narrow domain) with the skill of coming up with general-purpose neural network architectures, capable of extracting even better features from (almost) raw data.</p><p>The AUC scores on validation are above ~0.80, but slightly worse than previous models. There are two TODOs in code cell <strong><em>[30]</em></strong> with suggestions for improving the model. (I quick thought: “Two TODOs” would be a great name for a band! No? Never mind…)</p><p>But that’s not all; you can:</p><ul><li>experiment with a different recurrent units (GRU is hard-coded in the<br>RNNSiameseModel._create_base_network method defined in cell <strong><em>[29]</em></strong>);</li><li>change the number of neurons of the recurrent unit (again, it’s hard-coded);</li><li>increase the number of epochs;</li><li>add regularization;</li><li>surprise us with something else entirely.</li></ul><h3>Results on the test set</h3><p>OK, we have our best_*_pipe models trained and ready, we can test them on the non-registration data, for user specified in the test_user_names list at the beginning of the notebook.</p><p>Scroll to the code cell <strong><em>[31]</em></strong>, where you’ll find the test_user_verification function, which takes care of testing, and produces a list of AUC scores for the verification process. You can go through the function to fully understand its mechanics, but what’s important is that:</p><ul><li>this time, we use the registration sequences as “templates”;</li><li>for a new (non-registration) sequence, we pair it up with every registration sequence for that user;</li><li>for each pair we take the model and estimate the probability that it comes from the same user;</li><li>finally, we take the mean of the predictions, and take that as our final prediction of the new sequence belonging to the legitimate user.</li></ul><p>Results for our best_*_pipe-s can be produced by running cells from <strong><em>[33]</em></strong> to <strong><em>[36]</em></strong>, and behold:</p><ol><li>XGBoost has the highest mean AUC (0.9880);</li><li>The MLP model is second (0.9856);</li><li>Simple siamese model is third (0.9272), but one user had “definitely wrong” predictions;</li><li>And our most complicated model is fourth (0.8907).</li></ol><p>At first glance, it seems that simplicity pays off. But notice that XGBoost is far from simple, and it wouldn’t perform as good if we didn’t know that dwell and flight times are valuable for verifying users. If we used only the aggregated statistics of the first order difference, XGBoost’s mean AUC score would drop to ~0.91, and that’s not that far from the performance of the siamese recurrent neural network.</p><p>Now, on the other hand, we can give you a hint that a single change to the RNNSiameseModel can pull the mean AUC score on the test to ~0.94 (without adding dwell/flight times). Can you guess what is it?</p><p>And more importantly: can you achieve an even higher score with the RNNSiameseModel?</p><h3>Parting words</h3><p>If you’ve managed to go through the whole blog post — congratulations! It was a long one, but hopefully it was worth it.</p><p>If you have any questions regarding this blog post, add a comment. But if it’s more code-related, we would appreciate it if you opened an Issue at the workshop <a href="https://github.com/nethone/keystroke-dynamics">repo</a>.</p><p>And of course, if you manage to enhance the siamese architectures, you can fork the project, and provide a link to your version in the comments for everyone to see!</p><p><strong>About Nethone</strong></p><p><a href="http://www.nethone.com/">Nethone</a> creates A.I.-based anti-fraud and business intelligence solutions that help enterprises from all around the world turn threats into well-informed, profitable decisions. Founded in 2016 by experienced data scientists, security experts, and business executives, Nethone is one of the fastest-growing FDP companies in Central Europe.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*KtnDoZhaAkXhmLy0-y_JoA.png" /></figure><p>If you enjoyed this post, please hit the clap button below 👏👏👏</p><p>You can also follow us on <a href="https://www.facebook.com/Daftcode/">Facebook</a>, <a href="https://twitter.com/Daftcode_pl">Twitter</a> and <a href="https://www.linkedin.com/company/daftcode-sp-z-o-o-">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ba807029f74" width="1" height="1" alt=""><hr><p><a href="https://medium.com/daftcode-blog/building-supervised-models-for-user-verification-part-2-of-the-tutorial-1ba807029f74">Building Supervised Models for User Verification — Part 2 of the Tutorial</a> was originally published in <a href="https://medium.com/daftcode-blog">Daftcode Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Supervised Models for User Verification — Part 1 of the Tutorial]]></title>
            <link>https://medium.com/daftcode-blog/building-supervised-models-for-user-verification-part-1-of-the-tutorial-7496d5d394b9?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/7496d5d394b9</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[fraud]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[neural-networks]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Tue, 23 Jan 2018 13:54:29 GMT</pubDate>
            <atom:updated>2018-03-02T10:34:43.418Z</atom:updated>
            <content:encoded><![CDATA[<h3>Building Supervised Models for User Verification — Part 1 of the Tutorial</h3><h4>Introduction to the data set and validation</h4><p>Written by <a href="https://medium.com/u/e51fd42b6648">Maciek Dziubiński</a> and <a href="https://medium.com/u/c2e894061ac2">Bartosz Topolski</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f5vZzNaCyPSTzZib5YSgNQ.png" /><figcaption>Illustration by <a href="https://dribbble.com/magdalenatomczyk">Magdalena Tomczyk</a></figcaption></figure><p><strong>tl;dr</strong> We’ll develop four supervised learning models for deciding whether a pair of behavioral patterns comes from the same user. Those models will be: 1) XGBoost, 2) MLP neural network, 3) simple siamese neural network, 4) siamese recurrent neural network.</p><p>We <strong>recently held a workshop at the </strong><a href="http://plinml.mimuw.edu.pl/#agenda"><strong>“PL in ML” conference</strong></a><strong> entitled “User identification based on keystroke dynamics”. The workshop introduced the problem of verifying a user with the help of behavioral data collected during typing in a password on a keyboard. Materials from the workshop are available </strong><a href="https://github.com/nethone/keystroke-dynamics"><strong>here</strong></a><strong> but you can </strong><a href="https://mybinder.org/v2/gh/nethone/keystroke-dynamics/master?filepath=workshop.ipynb"><strong>run the code right away</strong></a><strong> by the grace of this new, hot service called </strong><a href="https://mybinder.org/"><strong>MyBinder</strong></a><strong>. The idea is that you go through the notebook as you read this blog post, so it pays off to click “Cell –&gt; Run All” pretty much now.</strong></p><p>This blog post serves as a high-level intro to the problem, an overview of the models, and a commentary on what more can be done to improve the models. But more importantly: the <strong>purpose</strong> of the workshop (and this blog post, as a consequence) is to give you, the Reader, a piece of working code that you can play with, modify, enhance, and run as easily as possible.</p><h3>The set-up</h3><p>A seamless user verification system would significantly improve the security of our e-accounts, without sacrificing any of their conveniences. The idea is to reinforce the standard user verification system (typically: a password) with a system that verifies whether the user’s behavior matches that of the legitimate owner of an account.</p><p>Suppose that you’ve collected behavioral data of every user during registration. Now, some time after the registration <em>someone</em> logs into the account. <strong>We would like to be able to say whether the behavioral pattern of this “someone” matches the behavioral pattern of the owner of the account.</strong></p><p>We’ll focus on one particular type of behavioral data, namely: the so-called <strong>keystroke dynamics</strong>, which are sequences of timestamps corresponding to push/release events recorded during typing. We will train four binary classification models, which for a pair of keystroke dynamics will predict whether it was produced by the same user. Using such a model, we’ll check if a new behavioral pattern matches those collected during registration, and based on that we’ll estimate the probability that we are indeed dealing with the account owner.</p><h3>Keystroke dynamics</h3><p>In a general sense, keystroke dynamics relates to the detailed timing information describing when each particular key was pressed and released as a person is typing. But here we’ll be working with a less detailed sequence of timing information, namely: we’ll only have access to the timing information, without knowing exactly which key was being pressed/released.</p><h3>The data set</h3><p>We’ll be using the <a href="http://mpl.buaa.edu.cn/detail1.htm">Bei Hang data set</a>, but you don’t need to download it, it’s already included in the workshop repo, in the BeiHang directory. There are two sub-sets of the data: DatasetA and DatasetB — the first one was collected in a cyber cafe environment, and the latter in laboratory conditions. We’ll be working with DatasetA since it’s more “realistic”.</p><p>The data set is comprised of timestamp sequences of the form: <br><em> P1, R1, P2, R2, … , Pn, Rn</em><br>where <em>Pi</em>, is the timestamp of <em>pressing</em> the <em>i</em>ᵗʰ key, and <em>Ri</em> is the timestamp of <em>releasing</em> the <em>i</em>ᵗʰ key. For each user, we have 4 or 5 such sequences collected during registration, and about 20 sequences recorded during log-in attempts. Among those ~20 sequences some came from the actual owner of the account, while other were typed in by imposters.</p><p>The data are read into a pandas.DataFrame using the produce_whole_DF function (code cell <strong><em>[3]</em></strong>), so that you can focus more on the models themselves, rather than the process of loading and cleaning the data.</p><p>Example keystroke dynamics collected during registration for two users (“zhuzhu” and “qianhai18”) are plotted in code cells <strong><em>[5]</em></strong> and <strong><em>[6]</em></strong>. Additionally, there are also plots for the first order difference of timestamps (calculated using the np.diff function). What I mean by first order difference is a sequence out produced from sequence seq in the following manner: out[n] = seq[n+1] — seq[n], or in a single step: out = np.diff(seq).</p><h4><strong>Keystroke dynamics features</strong></h4><p>There are two prominent characteristics of the keystroke dynamics: the dwell and flight times. The dwell time is the duration of a key being pressed, while the flight time is the time period between releasing a key and pressing the next key.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a3r5CNv1RFKTeMqe-sMlpQ.png" /></figure><p>Dwell and flight times are calculated using functions defined in code cell <strong><em>[7]</em></strong> and their values (again, for users “zhuzhu” and “qianhai18”) are plotted in code cells <strong><em>[8]</em></strong> and <strong><em>[9]</em></strong>.</p><p>Now, these sequences (keystroke dynamics, dwell and flight times) differ in length between users, whereas most supervised models require feature vectors of unified length. So, in order to satisfy these requirements, we’ll need to somehow aggregate (or: summarize) these sequences of variable lengths. We’ll take the mean and the standard deviation of the dwell times, of the flight times, but also of the first order difference. These features are calculated in the extract_features function (code cell <strong><em>[7]</em></strong>), yielding a single feature vector of length=6 for each keystroke dynamics sequence. But notice that within the extract_features there is a comment:<br> # TODO(1): add more features<br> TODOs like this one are scattered throughout this notebook, and the number (1, 2, or 3) stands for difficulty, 1 being “easy”, and 3 being “hard/complex”.</p><h4><strong>Validation</strong></h4><p>It’s easy to trick oneself into thinking that the model works fine, when in fact its performance is due to some sort of data leakage. Thus, it’s important to split the data into train and test sets <em>according to user names</em>. Otherwise, the performance on the test set would be an over-optimistic estimation of how well the model performs for new, unseen data (in this case: new users).</p><p>That said, the Bei Hang data set is not exactly the cleanest, error-free collection. To make sure the results reported on the test set are not prone to defects in the data, I hand-picked a set of users names, test_user_names (code cell <strong><em>[12]</em></strong>), which we’ll use to define the test set, and the train set as its complement. This is not quite legit — I’m using some arbitrary criteria for choosing the train and test sets, rather than focusing on making both of them unbiased samples of the underlying distribution. However, for the purpose of this blog post, let’s assume that the keystroke dynamics are indeed <em>i.i.d.</em> for different users, and that there is no confounding factor causing certain users to have some of their sequences damaged.</p><p>But going back, we’ve mentioned in <em>The set up</em> section that we’ll train binary classification models. For that, we’ll pair up the observations, and label them: 1 if the pair belongs to a single user, and 0 otherwise. Training and validation will be performed only on pairs of observations collected during registration — see the DF_registration_train variable in the cell <strong><em>[12]</em></strong>. (BTW, throughout the code the “DF_” prefix indicates that the variable is a pandas.DataFrame.)</p><h3>Models</h3><p>We will train four models:</p><ol><li>XGBoost;</li><li>Multi-layer perceptron (MLP);</li><li>Simple siamese neural network;</li><li>Siamese recurrent neural network.</li></ol><p>In terms of what the model takes as input, we are dealing with two very different types of models. The first type (XGBoost &amp; MLP) are models which we’ll supply with absolute differences of features extracted from keystroke dynamics sequences:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QOUL7xMud5n7GO_WHee4TA.png" /></figure><p>and in code calculated simply as: abs_diff = np.abs(a — b). The labels denote whether the two sequences come from the same user. In other words: we’ll train the model on a feature matrix, whose rows denote how two keystroke dynamics sequences differ.</p><p>The second type of models (the siamese models) are those that take as input full information describing both observations:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AgyUgOKf229ojRNf-kJEmQ.png" /></figure><p>The “Simple siamese neural network” will take two concatenated feature vectors (each describing a keystroke dynamics sequence), and that’s exactly what’s being shown in the image above. However, the “Siamese recurrent neural network” will take two <em>almost</em> raw sequences describing the two keystroke dynamics. The “almost” is because they’re actually going to be the first order differences of sequences of timestamps (recall the np.diff function).</p><h4>Model training and validation</h4><p>To train and validate the model we’ll use the custom_cross_val_auc_score function (code cell <strong><em>[15]</em></strong>). What’s “custom” about it is that for each fold it splits the data into train and validation sets, according to user names. This way, if a given user was used for training, it won’t be used for validation.</p><p>The first argument of this function, estimator, is an object with two methods: estimator.fit and estimator.predict_proba. Technically, most classifiers available in sklearn implement these methods (but not all have the latter; see for example sklearn.svm.LinearSVC), so it seems that estimator might simply be a bare-bone classifier. However, the estimator object is also required to have additional attributes: estimator.only_feature_diffs and estimator.feature_names. The first one is used to determine whether the classifier will take two feature vectors, one per each keystroke dynamics sequence, or rather their difference. The latter attribute is used to locate which columns of the input frame (DF_registration) are used as features.</p><p>But we might need to standardize the data or transform it before passing to the classifier. To retain the attributes expected of the estimator object, we can wrap a list of preprocessing and transformation steps (along with a classification step at the end), with the sklearn.pipeline.Pipeline class. This is pretty standard practice, and if you haven’t encountered it previously — now’s your chance. Then, we can add the estimator.only_feature_diffs and the estimator.feature_names attributes. And that’s exactly what we’re going to do when training our models in code cells: <strong><em>[16]</em></strong>, <strong><em>[21]</em></strong>, <strong><em>[26]</em></strong>, and <strong><em>[29]</em></strong>.</p><h4>1. XGBoost</h4><p>The xgboost.XGBClassifier is a robust model in that it gives good results out-of-the-box for a wide range of problems, without the need of adjusting the hyper-parameters. We won’t be able to go into details of how XGBoost is trained in this blog post, but for perhaps the best introduction of the algorithm, make sure to check out <a href="http://xgboost.readthedocs.io/en/latest/model.html">this source</a>.</p><p>For our purposes it suffices to know that XGBoost offers a good base-line for our subsequent, neural-net-based models. If we’ll be able to reach XGBoost’s default performance without much meddling, we’ll consider a model “easy to handle”. We’ll see that, while multi-layer perceptrons are easy to handle, siamese architectures require significantly more care.</p><p>As an aside, one drawback of XGBoost is that it’s difficult to interpret and even with the aid of tools like <a href="https://github.com/marcotcr/lime">LIME</a> or <a href="https://github.com/TeamHG-Memex/eli5">ELI5</a>, understanding how exactly the model makes its predictions poses a challenge.</p><p>The XGBoost model is trained in a code cell <strong><em>[16]</em></strong>. We plot the AUC scores calculated by the custom_cross_val_auc_score as a histogram. And although I do admit a histogram of four values looks a bit silly, it becomes justifiable once you use a higher number of folds (the num_folds argument of the custom_cross_val_auc_score function). As a bonus, we can take a peek into which features were important for the construction of the XGBoost model, using the xgboost.plot_importance function. Sure enough, the means of dwell and flight times were the most relevant features, and it seems that they are quite informative as the model achieves high values of AUC scores of about 0.90 on validation.</p><p>For now, we’ll keep the best_xgb_pipe, and use it at the very end of this notebook, to see how well it performs on the test set, along with the rest of the models. You might play with the arguments of the xgboost.XGBClassifier, and get an even better score, but we would advise to leave that spunk for the siamese recurrent neural network.</p><h3>Summary</h3><p>We’ve introduced the data set, spent considerable amount of time explaining the loopholes associated with validation, and trained our first model, the XGBoost.</p><p>In Part 2, we’ll dive into neural-network-based models, and finally assess all models on the test set.</p><blockquote><strong>Read the second part of the tutorial:</strong></blockquote><p><a href="https://blog.daftcode.pl/building-supervised-models-for-user-verification-part-2-of-the-tutorial-1ba807029f74">Building Supervised Models for User Verification — Part 2 of the Tutorial</a></p><p><strong>About Nethone</strong></p><p><a href="http://www.nethone.com/">Nethone</a> creates A.I.-based anti-fraud and business intelligence solutions that help enterprises from all around the world turn threats into well-informed, profitable decisions. Founded in 2016 by experienced data scientists, security experts, and business executives, Nethone is one of the fastest-growing FDP companies in Central Europe.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*KtnDoZhaAkXhmLy0-y_JoA.png" /></figure><p>If you enjoyed this post, please hit the clap button below 👏👏👏</p><p>You can also follow us on <a href="https://www.facebook.com/Daftcode/">Facebook</a>, <a href="https://twitter.com/Daftcode_pl">Twitter</a> and <a href="https://www.linkedin.com/company/daftcode-sp-z-o-o-">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7496d5d394b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/daftcode-blog/building-supervised-models-for-user-verification-part-1-of-the-tutorial-7496d5d394b9">Building Supervised Models for User Verification — Part 1 of the Tutorial</a> was originally published in <a href="https://medium.com/daftcode-blog">Daftcode Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wrap Your Repo with Anaconda Project]]></title>
            <link>https://medium.com/daftcode-blog/how-to-wrap-your-repo-with-anaconda-project-c7ee2259ec42?source=rss-e51fd42b6648------2</link>
            <guid isPermaLink="false">https://medium.com/p/c7ee2259ec42</guid>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[anaconda]]></category>
            <dc:creator><![CDATA[Maciek Dziubiński]]></dc:creator>
            <pubDate>Thu, 28 Sep 2017 11:25:57 GMT</pubDate>
            <atom:updated>2018-03-02T10:50:16.656Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VtuVP1_6jcLdTog1StekQQ.png" /><figcaption>Illustration by <a href="https://dribbble.com/SoniaBudner">Sonia Budner</a></figcaption></figure><p><strong>tl;dr</strong> we take a repo with a somewhat convoluted README, wrap it up using the Anaconda Project, take care of the bookkeeping and publish our Project for everyone to download and run with a single command without worrying about dependencies.</p><p><strong>So, you’ve just finished your project — an analysis, a model, a web app, or something else entirely — and you would like to share it with the community. How to do it in the easiest way? In this post, we’ll explore and showcase the Anaconda Project — a (fairly) new tool from Anaconda, that allows you to pack your project along with runnable commands, self-resolving dependencies mechanism, environmental variables, and more.</strong></p><p>From the Anaconda Project docs:</p><blockquote><em>Anaconda Project encapsulates data science projects and makes them easily portable. Project automates setup steps such as installing the right packages, downloading files, setting environment variables and running commands.</em></blockquote><blockquote><em>Project makes it easy to reproduce your work, share projects with others and run them on different platforms. It also simplifies deployment to servers. Anaconda projects run the same way on your machine, on another user’s machine or when deployed to a Server.</em></blockquote><p>Sounds good, but does it work? Let’s give it a try!</p><h3><strong>Where to get it?</strong></h3><p>Anaconda Project is included in the standard Anaconda distribution (version 4.3.1 or higher), so to check your version, run:</p><pre>anaconda-project --version</pre><p>My initial, out-of-the-box version was 0.6.0, however, to get the latest version (0.8.0 which is used here), run:</p><pre>conda install anaconda-project --channel conda-forge</pre><p>Thus, the version of Anaconda Project I was working with was 0.8.0. Also, I used Python 3.6 and worked on Ubuntu 16.04.</p><h3><strong>Use case</strong></h3><p>At this year’s PyCon PL, I had a talk about transfer learning, where I showed how to combine several scripts and tools to retrain an artificial neural network and analyze its output. I wanted to identify motorcycle types from images and to assert that the model made its predictions based on motorcycle parts and not on artifacts hidden in the images. You can find the code from that presentation on <a href="https://github.com/daftcode/PyCon-motorcycle-transfer-learning"><strong>DaftCode’s GitHub</strong></a>.</p><h4>The repo contains:</h4><p>● a script for downloading images from Google Search;</p><p>● a runnable wrapper for the retrain.pyscript (from tensorflow/examples/image_retraining);</p><p>● an optional inspection step with TensorBoard;</p><p>● a Jupyter Notebook (which is the culminating point of the whole project) that:</p><p>1) displays patterns of activations in the last hidden layer of the network (in response to the different types of motorcycles),</p><p>2) makes predictions for not-yet-seen images (in the test_images/ directory),</p><p>3) shows an analysis of which parts of the images led the model to a particular prediction</p><p>● and a pretty long README file.</p><p>I wanted to show the steps leading to the analysis presented in the notebook to demonstrate how easy it is to adapt (retrain) models in TensorFlow. But the mere length of the README might suggest something different.</p><p>The reason is that there’s a fair amount of “bookkeeping” needed for the main analysis: ensuring all required packages are installed, downloading the data and retraining the model.</p><p>The idea is to use Anaconda Project to really “get to it” as fast as possible, simply by skimming over the bookkeeping part of the project and running everything with a single command. In this post, I’m going to show how to wrap an existing repo, without modifying it, in such a way that Anaconda Project: clones the repo, takes care of the bookkeeping and runs the final Jupyter Notebook for the User to play with. Alternatively, you might want to modify an existing repo and make it an Anaconda Project. Or, if you start a new project, it would probably be more convenient to make it an Anaconda Project and build it from the ground up, as you go along.</p><h3><strong>Initializing the Project</strong></h3><p>Let’s first create a directory for our Project, initialize it and add the repo with the PyCon presentation.</p><p>To initialize the Anaconda Project, run:</p><pre>mkdir pycon_presentation_anaconda_project<br>cd pycon_presentation_anaconda_project<br>anaconda-project init</pre><p>This creates the anaconda-project.yml file, which is the heart of the Project. This is the configuration file that will be modified after each anaconda-project command.</p><p>Next, we’ll clone the repo with the PyCon presentation:</p><pre>git clone --recursive <a href="https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git">https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git</a></pre><p>The --recursive flag will also update the submodule tensorflow within it.</p><p>To ensure reproducibility, we need to checkout a particular commit:</p><pre>cd PyCon-motorcycle-transfer-learning<br>git checkout 5c00c5b<br>cd ..</pre><h3><strong>Adding commands</strong></h3><p>But wait… we can automate the process of updating the submodules and checking out a particular commit. Let’s do that by adding the first command to our project:</p><pre>anaconda-project add-command clone_repo &quot;git clone --recursive <a href="https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git">https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git</a>; cd PyCon-motorcycle-transfer-learning; git checkout 5c00c5b&quot;</pre><p>We’ll be asked what kind of command is this:</p><pre><em>Is clone_repo a (B)okeh app, (N)otebook, or (C)ommand line?</em></pre><p>and since this is a command ran in the command line, we choose (C). We can also specify the command type with the --type option. Note, however, that in our case --type unix is the equivalent of choosing (C).</p><p>There’s no point in running the command (as we have already taken care of the submodules “manually”), but you could do it by typing:</p><pre>anaconda-project run clone_repo</pre><p>Because the directory already exists (you did clone it, right?), nothing is going to really happen. But you might also notice that you get an annoying message:</p><pre>Potential issues with this project:<br>* anaconda-project.yml: No commands run notebooks PyCon-motorcycle-transfer-learning/presentation.ipynb, ...</pre><p>and if you don’t like to be reminded about it <em>every single time you run any command</em>, you can edit the .projectignore file and add the following line:</p><pre>**.ipynb</pre><p>Next, we’ll add a command that downloads images needed for retraining. For that, let’s create the following script download_images.sh:</p><pre>cd PyCon-motorcycle-transfer-learning<br>for type in classic cross cruiser superbike<br>do<br>python image_download.py motorcycle $type<br>done</pre><p>that calls the image_download.py script in the PyCon-motorcycle-transfer-learning repo. To add this script to the Project’s commands, type:</p><pre>anaconda-project add-command --type unix download_images &quot;bash download_images.sh&quot;</pre><p>This command will download 400 images of four classes of motorcycles (100 each): classic, cross, cruiser, and superbike. Let’s run it:</p><pre>anaconda-project run download_images</pre><p>Now, there is a time for the workhorse of this Project — the retrain command:</p><pre>anaconda-project add-command --type unix retrain &quot;cd PyCon-motorcycle-transfer-learning; python retrain_wrap.py&quot;</pre><p>but in order to run it correctly, we need to add an additional package: tensorflow.</p><p>To add this package so that it’s loaded during the run along with other necessary packages, we’ll use the add-packagesfunctionality:</p><pre>anaconda-project add-packages tensorflow</pre><p>The retrain_wrap.py script actually calls the tensorflow/tensorflow/examples/image_retraining/retrain.py script, which downloads the necessary pre-trained model (by default the largest MobileNet available), splits the data into train, validation and test sets, computes the “bottlenecks” (activations on the neurons of the last hidden layer) and then takes care of the whole retraining procedure.</p><p>However, we should inspect how the retraining went. For example, we might have overfitted the training data and could have gotten a better model by running the procedure in fewer steps. To inspect the retraining we’ll use TensorBoard (for which the retrain.py script conveniently gathers summary statistics):</p><pre>anaconda-project add-command --type unix inspect_with_tensorboard &quot;cd PyCon-motorcycle-transfer-learning; tensorboard --logdir summaries&quot;</pre><p>Note, that you’ll need a browser to view the app at: <a href="http://localhost:6006/">http://localhost:6006</a>. Once you’re done with analyzing the graphs, hit Ctrl+C to terminate TensorBoard.</p><p>If you would like to deploy a web app to a server (TensorBoard, for example), it’s possible to customize the default behavior (as long as the app communicates via HTTP); for details see the Anaconda Project reference.</p><p>For our final command, we’ll add the presentation.ipynb notebook:</p><pre>anaconda-project add-command --type notebook presentation &quot;PyCon-motorcycle-transfer-learning/presentation.ipynb&quot;</pre><p>Again, we’ll need to add one tiny package to be able to run it: lime<em>. </em>But this is a little bit more tricky because we’re interested in the latest version — 0.1.1.23, which I used for this project — whereas the latest version at the conda-forge channel is 0.1.1.18. However, if we go to <a href="http://www.anaconda.org/">www.anaconda.org</a> and search for <em>lime</em>, we’ll see that there’s a channel called “viascience” that offers the coveted, latest version:</p><pre>anaconda-project add-packages --channel viascience lime</pre><p>Note, that this time we’re telling Anaconda where to look for it (the “viascience” channel).</p><p>We can now run the command:</p><pre>anaconda-project run presentation</pre><p>open the notebook and run all commands within it.</p><h3><strong>Putting it all together</strong></h3><p>OK, let’s list out the commands we’ve defined:</p><pre>anaconda-project list-commands</pre><p>The output should look something like the following:</p><pre>Name                   Description</pre><pre>====                   ===========</pre><pre>clone_repo             git clone --recursive <a href="https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git">https://github.com/daftcode/PyCon-motorcycle-transfer-learning.git</a>; cd PyCon-motorcycle-transfer-learning; git checkout 5c00c5b</pre><pre>download_images        bash download_images.sh</pre><pre>inspect_with_tensorboard  cd PyCon-motorcycle-transfer-learning; tensorboard --logdir summaries</pre><pre>presentation           Notebook PyCon-motorcycle-transfer-learning/presentation.ipynb</pre><pre>retrain                cd PyCon-motorcycle-transfer-learning; python retrain_wrap.py</pre><pre>retrain                cd PyCon-motorcycle-transfer-learning; python retrain_wrap.py</pre><p>Great, but… what now? Should we then replace our previous README with several anaconda-project run ... calls? Or can we do better?</p><p>For me, <strong>better</strong> means: <strong>shorter</strong>, so that my README says literally this: “To run the Project, type: anaconda-project run all”. Frankly, I was expecting there would be a functionality that allows anaconda-project runto run all or several commands in some defined order. But come to think of it, we might simply define an all command like this:</p><pre>anaconda-project add-command --type unix all &quot;anaconda-project run clone_repo; anaconda-project run download_images; anaconda-project run retrain; anaconda-project run presentation&quot;</pre><p>(I’ve left out the anaconda-project run inspect_with_tensorboardsince it requires that the User hits “Ctrl + C”, which, unfortunately, kills the rest of the anaconda-project process.)</p><p>We can now formulate our README.md file:</p><pre># To run the project ...<br>... type:<br>```<br>anaconda-project run all<br>```<br>## To inspect the retraining process ...<br>... type (after running `all`):<br>```<br>anaconda-project run inspect_with_tensorboard<br>```</pre><p>That’s what I call a short README!</p><h3><strong>Uploading the Project to Anaconda Cloud</strong></h3><p>Before we upload the Project, we want to make sure it’s as tidy as possible. We can use the:</p><pre>anaconda-project clean</pre><p>command to get rid of the envs/ directory with all the required libraries, but notice it left out the PyCon-motorcycle-transfer-learning repo. To exclude it manually, open the .projectignoreand add the following:</p><pre>PyCon-motorcycle-transfer-learning</pre><p>Note, that the clone_repo command will take care of cloning this repo (along with its submodule tensorflow) and checking out the right commit.</p><p>Now, once our project is as tidy as it can be, let’s upload it with the following, final command:</p><pre>anaconda-project upload</pre><p><em>Et voila, </em>our project is out there: <a href="https://anaconda.org/mdzi">https://anaconda.org/mdzi</a> and once it’s downloaded, in can be unpacked with:</p><pre>anaconda-project unarchive pycon_presentation_anaconda_project.tar.bz2</pre><p>Pretty neat :)</p><p>For a final touch, I’ve also uploaded the presentation.ipynb notebook to Anaconda Cloud with:</p><pre>anaconda upload presentation.ipynb</pre><p>so that you can see the final (though static) result of the Project right away.</p><h3><strong>Summary</strong></h3><p>We now know how to use Anaconda Project to wrap up an already existing repo, add <em>commands </em>and<em> dependencies, </em>so that the User can download the Project and run everything with a single command:</p><pre>anaconda-project run all</pre><p>Anaconda Project is easy to work with and really allows you to simplify the flow for an end User.</p><p>However, if the set-up required for running your project isn’t complicated, you might simply export your environment (instead of defining anaconda-project commands), write a bash script run.sh packing it all up, so that the README would look something like this:</p><pre>conda env create -f environment.yml<br>bash run.sh</pre><p>But Anaconda Project isn’t just adding commands and specifying dependencies— it allows you to: automatically download large files (that you don’t want to keep in version control), pack up web apps (that communicate with HTTP) and run them on a server, handling environmental variables that contain credentials and adding services like Redis (this feature is in a demo phase, though).</p><p>It’s worth to try it out and see how you can use it for your Project!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*KtnDoZhaAkXhmLy0-y_JoA.png" /></figure><p>If you enjoyed this post, please hit the clap button below 👏👏👏</p><p>You can also follow us on <a href="https://www.facebook.com/Daftcode/">Facebook</a>, <a href="https://twitter.com/Daftcode_pl">Twitter</a> and <a href="https://www.linkedin.com/company/daftcode-sp-z-o-o-">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c7ee2259ec42" width="1" height="1" alt=""><hr><p><a href="https://medium.com/daftcode-blog/how-to-wrap-your-repo-with-anaconda-project-c7ee2259ec42">Wrap Your Repo with Anaconda Project</a> was originally published in <a href="https://medium.com/daftcode-blog">Daftcode Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>