<?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 Michael Sugimura on Medium]]></title>
        <description><![CDATA[Stories by Michael Sugimura on Medium]]></description>
        <link>https://medium.com/@michaelsugimura?source=rss-5d6bed290a7c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*NfQ63Ntd5aRUR_gCIBNhJQ.jpeg</url>
            <title>Stories by Michael Sugimura on Medium</title>
            <link>https://medium.com/@michaelsugimura?source=rss-5d6bed290a7c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 30 May 2026 09:13:23 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@michaelsugimura/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[A Tricksy Look at Outfitting: Kitsune]]></title>
            <link>https://medium.com/@michaelsugimura/a-tricksy-look-at-outfitting-kitsune-2dabff17c690?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/2dabff17c690</guid>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[fashion]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Fri, 19 Mar 2021 22:03:35 GMT</pubDate>
            <atom:updated>2021-03-19T22:03:35.676Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hqtLsTDFDQxoLv1n6CQDQg.png" /></figure><p>This blog is based on a hack week I did near the end of 2020. For background, the ShopRunner Data Science allows members to spend a week per quarter working on a ShopRunner-related topic of their choice.</p><p>Ever since getting involved in e-commerce data science, one of the things I have been thinking about has been automating outfit creation. Starting to work at ShopRunner, my general focus has been helping the team build out more deep learning and, in particular, computer vision expertise. For the first year and a half or so, the focus was on building out those foundational capabilities. I personally had a lot of fun working on multi-task learning pipelines (<a href="https://medium.com/shoprunner/tonks-building-one-multi-task-model-to-rule-them-all-3e5d020f1f2b">open sourcing our ShopRunner multi-task library</a>, <a href="https://towardsdatascience.com/two-tasks-two-datasets-one-network-multi-task-learning-with-dnd-eaef00b5e741?sk=113c9f9d9180a577e055242be6fb6bd2">multi-task multi-dataset</a>, <a href="https://towardsdatascience.com/tuning-a-multi-task-fate-grand-order-trained-pytorch-network-152cfda2e086">pytorch multi-task learning</a>, and <a href="https://towardsdatascience.com/multi-task-learning-on-fate-grand-order-with-keras-261c5e8d3fb8">keras multi-task learning</a>) and am proud of how the team has turned those internal pipelines into an open source library called Octopod (<a href="https://github.com/ShopRunner/octopod">github</a>, <a href="https://pypi.org/project/octopod/">pypi</a>). However, once that foundation was laid, the focus then started to shift towards things like visual similarity, user-uploaded visual search, and outfitting.</p><p>The idea of outfitting is that, for a given seed product, we should be able to return a set of complementary products that go along with it to create an outfit. For me, outfitting is an interesting problem area because it isn’t that well-defined or mapped out, and in these spaces where there are no real right answers or accepted methods, it means I can satisfy my thirst for adventure and shenanigans.</p><p>There are a number of reasons that outfitting in general is a fairly hard problem:</p><ol><li>It’s hard to find large datasets of curated outfits.</li><li>The model needs to be able to generalize to new and unseen items.</li><li>Even if you find/acquire a dataset, you run the risk of it not working well for your in-house dataset/catalog.</li></ol><p>In terms of datasets, one of the most-frequently used ones has been one from a website called Polyvore (dataset <a href="https://github.com/xthan/polyvore-dataset">here</a>), which has a good number of products and has been used a lot in academic literature. Another newer dataset which was really cool to see is one from Alibaba from their paper <strong>POG: Personalized Outfit Generation for Fashion Recommendation at Alibaba iFashion </strong>(paper <a href="https://arxiv.org/abs/1905.01866">here</a> &amp; dataset <a href="https://github.com/wenyuer/POG">here</a>). This POG dataset contains around 500K unique items and 1M outfits, so it is a great resource for the community. So for this hack week, I decided to focus my efforts on using outfits from the POG dataset for outfitting.</p><p>Even though the POG dataset helps with that initial hurdle of finding curated outfits, it still doesn’t necessarily solve the other two problems of 1) generalizing to new and unseen items and 2) being relevant for a given new catalog of items. Some things that can cause issues between catalogs could be differing photography styles, product image compositions, model positions (the person kind, not the math kind), etc. between the two catalogs.</p><p>This is where I think the work and perspective I have from working on other projects like visual search and user-uploaded visual search comes in. Both of these work by taking vectors and doing cosine similarity searches on our full ~7M product catalog. The idea is that we extract vectors from images using models we have fine-tuned to internal tasks as the underlying representation of our products. This lets us handle never before seen products since we can always get vectors for them. However, my one remaining concern was that while we could use vector representations of the products in an outfit and train a model to generate vectors of products that go along with the initial item, an external dataset might not map well onto an internal ShopRunner dataset or product catalog.</p><p>I tend to enjoy clever and interesting ways to attack problems, and every good project needs a nickname. I nicknamed this hack week project, and in particular the model, Kitsune. Partially because I like 9 tailed foxes from East Asian mythology and their trickster nature, but I also liked the idea of a creature with multiple tails representing my model (you will see how later). Now, off on an adventure as I go through my hack week project, Kitsune.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fdzDOYB-75kb0GIIYSEkpA.png" /><figcaption>Kitsune outfitting example</figcaption></figure><h3>Rethinking Outfitting</h3><p>For as long as I have been thinking of outfitting, we have been thinking of the problem as:</p><blockquote><em>“the goal of outfitting is for a given seed product we want to be able to recommend other products that go along with it”</em></blockquote><p>There is nothing wrong with this statement since it is factually what we are trying to do, but it also constrained my point of view because it was so well-established. In particular the focus on returning a set of products.</p><blockquote><em>“the goal of outfitting is for a given seed </em><strong><em>product</em></strong><em> we want to be able to recommend other </em><strong><em>products</em></strong><em> that go along with it”</em></blockquote><p>What this focus on returning a product meant is that any model has to be able to return the necessary information for a full product. But what if our model didn’t? The way that we typically think about that from a computer vision point of view at ShopRunner is via a combination of vectors from our in-house taxonomy and attribute models. If you think about models as employees in a department store, then a taxonomy model tries to sort products into their respective sections (jeans, shirts, dresses, shoes, and so on), while an attribute model aim to sort products within each of those sections (into things like color, pattern, dress length, sleeve length, season, etc.).</p><p>However this focus on attribute and taxonomy vectors sort of constrained my ability to think of different ways to approach the problem. So all of my brainstorming basically looked like the below gif from Big Hero 6.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*OB6t-oD-HP2CVH3bNnIZvg.gif" /></figure><p>So I needed to shake things up.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/245/1*ZZuGYs3Z7dw4lE1BZWKwdA.gif" /></figure><p>For me, a lot of my breakthroughs at work come when I am able to take a step back and do other things and basically let my brain wander. A lot of that comes down to my martial arts (these days it’s a lot of practice on my Wing Chun wooden dummy), or my other hobbies like cooking. This time, oddly enough, it came after a fairly heavy Wing Chun workout when I had a dream about this outfitting problem. The general gist of it was what if we didn’t think about taxonomy and attributes together, but just the attributes?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*169P0qy1vhk_7Cc_IfWnAg.png" /><figcaption>Can we just represent an outfit by its attribute vectors? Grey shirt, black pants, &amp; black shoes vs grey, black, &amp; black.</figcaption></figure><p>This was an interesting idea because attributes like the ones I listed are not bound to a particular item of clothing, but rather apply generally across all types of clothing. So if we made a model that just learned to generate attribute vectors that go with the attribute vector of a seed item, then that model wouldn’t be tied to the structural taxonomy information of the base dataset.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*0at64M82TFK3KWm0SQgwWw.gif" /></figure><p>The remaining problem that I created with this pipeline is that attribute vectors by themselves are pretty useless for finding actual <strong>products</strong>, by design. In a taxonomy type of problem, the model learns how to differentiate products at a very structural level (pants, dresses, shoes, etc.). For attributes, they are trained to ignore structural information in favor of the details across a wide variety of types of products for things like color and pattern.</p><p>So my thoughts on how to re-add this structural component was just to make a dictionary of mappings from a taxonomy of a seed item (which we could always get in our catalog) to a set of taxonomies that could reasonably go well with it. For example, a seed taxonomy could be <em>dresses</em>, and the ones in the mapping might be <em>ankle boots</em>, <em>shoulder bag</em>, and <em>jacket</em>, since each of these mappings can reasonably be in an outfit with a dress. The main risk I was taking here was whether or not attribute vectors generated with some taxonomy in mind could be applied to an arbitrary new one, but those risks are what hack weeks are for!</p><h3>Setup and Preprocessing</h3><p>In terms of modeling, there are a lot of possible paths, particularly around transformers which have been used in papers like the Alibaba one that I mentioned earlier. The tactic there is to train a transformer to generate the next item in the outfit based on some seed item or current outfit state. However, a lot of my work at ShopRunner has been around building out multi-task learning pipelines, so I wondered if I could throw this pipeline at this outfitting problem.</p><h4>Dataset</h4><p>One of the real time savers in doing this is that one of my colleagues had used a subset of the Alibaba dataset for modeling work which focused on length 4 outfits and had stored the taxonomy and attribute model vector representations of the items. All in all this left us with around 50K outfits for our use.</p><p>So with the dataset in place, my goal was to build a multi-task model that can take in a seed item and generate 3 attribute vectors that go along with the seed item.</p><h4>Vector Compression</h4><p>The raw attribute model vectors are length 256, but we have also previously found that using PCA or another dimensionality reduction technique helps us increase the speed of our subsequent cosine similarity searches and space required to store the vectors with very little performance decrease. I also figured that a compressed representation would mean a more concise vector for my generator model to learn. So, I went ahead and compressed each item vector via PCA so each outfit was represented by 4 length 50 attribute model vectors.</p><h4>Data Augmentation</h4><p>Once I had my outfits of length 4 where each element was a length 50 vector, I went ahead and did some data augmentation. For this model pipeline, I made the conscious choice to have a single outfit for each unique seed item vector (around 95K unique items). This means that I have more outfits than my original 50K (now around 95K), but less than if I took all available permutations.</p><p>The reason I made this choice is based on some intuition I have (that could be wrong, but that is what hack weeks are about) that with this approach, if you take all permutations of every outfit, there are multiple right answers for every given seed item.</p><p>This intuition is based on some initial testing I was doing with my pipelines, where I used every permutation of every outfit. This gave me a ton of additional data and I thought it would be helpful. However, with this setup every task head not only generated basically the same vectors (because their datasets are all the same now, just shuffled), but the items they yielded were basically always the same. This could be due to the lack of ability of the model to handle the complexity of the dataset, but I found that changing the architecture didn’t affect these results. My results got cleaned up when I didn’t permute the dataset. By just using the base dataset without any permutations it meant that every task head had a unique dataset associated with it, and every item was appearing more or less a single time. So what I decided to do was a partway measure between these two passes. First, I would permute the dataset and then remove duplicates so that every seed item would only appear one time, but every item would have an outfit associated with it. This gave me more data than not permuting at all but didn’t have the duplicates that I found to be detrimental to this training pipeline.</p><p>So while this approach does give us less data to work with, it does make it so each task head should give a more unique specific answer for a given seed item and the task heads are not taught to clash with one another.</p><h3>Modeling: Modifying Octopod to Train Kistune</h3><p>The actual modeling pipeline is a standard Octopod training pipeline where I made custom model and dataset classes. In terms of structuring the training pipeline, Octopod is a multi-task multi-dataset pipeline where generally, we have a different dataset for each task head. In this case, each of the three task heads has a dataset where the <em>x</em>’s are all the seed items in the dataset while the targets are the vectors for the item in that first, second, or third slot in the outfit. The model class for Kitsune is just a simple model made up of a few linear layers stacked on one another with inspiration from the decoder section of an autoencoder, where the model takes in a length 50 vector and increases in size for a few layers and finishes in with 3 task heads each of length 50. The goal of the model is to take in a length 50 seed attribute vector and generate 3 length 50 vectors which can be used to find products.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/832/1*eIMK0UqN1p4uPxgUSRUSLA.png" /></figure><p>Next I just let the model train while cycling the learning rates for a few hours on a 2080Ti GPU.</p><h3>Constructing a Pipeline to Bring Kitsune to Life</h3><p>So I mentioned before that an upside to this approach to outfitting is that by just focusing on attributes instead of both attributes and taxonomy would be a useful way to learn on an arbitrary dataset and apply it to an in-house one. However, a downside that I also mentioned is that attribute vectors are pretty worthless on their own for visual similarity tasks because they don’t encode any structural information which makes it hard to get similar looking items out of them. So in addition to the model I trained above I made a mapping of seed item taxonomy categories to different taxonomy categories. For example:</p><pre>mapping_dict = {<br>    &#39;Dress&#39;: [&#39;Ankle boot&#39;, &#39;shoulder bag&#39;, &#39;leather jacket&#39;],<br>    &#39;T-shirt&#39;:[&#39;jeans&#39;, &#39;running shoes&#39;, &#39;hoodie&#39;]<br>}</pre><p>So, with the mapping_dict above, if a seed item was a dress, the task head 1 vector should look for Ankle boot items, task head 2 should look for shoulder bag items, and task head 3 should look for leather jacket items. First round of mappings were pretty basic and arbitrary, but with later versions, I made a few different examples for each of our several hundred taxonomy categories based on looking at WAY TOO MANY outfits online.</p><p>The core part of Kitsune is the model I trained above plus the mapping_dict which allows us to get a vector and taxonomy category. These two pieces are what we need to search our product catalog and find relevant items.</p><p>Once I had my mapping in place, I constructed a pipeline (see below) where I could grab an item from our product catalog for its length 50 attribute vector and taxonomy category. Run the seed item vector through my model which results in three separate length 50 vectors. For each vector from my model, I then get a mapping from the mapping_dict to get the three taxonomy categories for each of the three output vectors. Then I search our elasticsearch backed product catalog for similar vectors using cosine similarity, filtered down to a specific taxonomy for each of the three output vector + taxonomy pairs, which yields a sort of outfitting model.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*mDaHp2SwJd_DZD4RgAsB7Q.gif" /></figure><p>This pipeline was not too bad to build, mostly because our DS team and the other engineering teams at ShopRunner have been working quite hard to improve our pipelines, databases, and our ability to leverage elasticsearch for problems like visual similarity.</p><h4>Results</h4><p>An interesting part of this pipeline is that I had very minimal ways to visually sense-check the results of the pipeline until I finished my pipeline to query and visualize results from elasticsearch. When I finally reached this point, I was pleasantly surprised because I found that my hypotheses had held up reasonably well!</p><h4>Checking individual results</h4><p>So for the example below of the seed shoe on the left, the Kitsune model took in its vector and output 3 vectors for items that Kitsune thought went well with the seed item vector. Then, those 3 vectors get paired with 3 taxonomy categories based on my mapping I built and those two things are used to do cosine similarity searches of our ~7M item product catalog. The results are pretty reasonable/good! (I would normally say “not completely terrible”, but I realize I need to scale my reactions to things to be more generally applicable at times.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uElSPOfIxs1YcdU_R93hvg.png" /></figure><p>Something that I was curious about was whether or not these models would have a tendency to yield outfits containing more neutral items, which they do, like in the sample below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rxod8lzYAqCD6RbBLs0T6g.png" /></figure><p>The red dress gets paired with a bunch of black items. However if you think about it, this isn’t particularly <em>bad</em> behavior, since it is a reasonable way to dress. It also has the benefit of making the outfits more widely applicable to potential shoppers if the items are more generic/universal.</p><p>However, Kitsune does yield some more flavorful items, particularly when the base items are more generic in nature. So for the below brown trench coat (I guess it has been classified as a peacoat, but that is fine…) the middle row contains a number of different colored boots. For me this is fairly interesting because I initially thought that the vectors out of Kitsune would behave more like standard vectors out of our attribute models that encode things fairly concisely, but seeing that some of the vectors have a variety of colors and other characteristics was an interesting find.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ezlPtPQL8FD6tkr1AmpXLQ.png" /></figure><h4>Mixing and Matching Vectors and Taxonomy</h4><p>So besides wondering whether or not this method would yield anything interesting, I had a number of other thoughts about it going in. An interesting use case I had been thinking about was whether or not you could generate a set of vectors, get a set of 3 taxonomy categories, and then just mix and match all of them around. So if our original set of vectors and taxonomy categories looks like:</p><pre>Item 1 + taxonomy 1</pre><pre>Item 2 + taxonomy 2</pre><pre>Item 3 + taxonomy 3</pre><p>and this is a valid set of items to make up an outfit, then would shifting the taxonomy categories again also yield a valid outfit, such as:</p><pre>Item 1 + taxonomy 3</pre><pre>Item 2 + taxonomy 1</pre><pre>Item 3 + taxonomy 2</pre><p>If this turned out to be true, then it means we can generate 3 distinct outfits for a given seed item using a single mapping.</p><p>The next three images are for the same blue dress on the left and same vectors out of Kitsune, but what I did was shuffle around the taxonomy categories to see how the results looked and varied. I was mostly curious if all the results looked reasonable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*u1C9v_AO-Lz3hkZfZs9K-w.png" /></figure><p>This first one seems reasonable, the top vector encodes things like black, grey, and white by the looks of it, and the shoes follow a similar fairly neutral pattern.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TM38cGiXU-wpm1jFSPAFHA.png" /></figure><p>This second one is interesting because the middle vector has some red encoded into it, which we didn’t see show up in the initial set of items. However, these all work reasonably well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VroIBHhKJvmliOhnsfbpUA.png" /></figure><p>In this third set, like the second above, we see that there are red items showing up in the middle set of results, even in the first image which shows a person (it is technically for the bag they are holding, I just fetched an image with a person modeling the bag).</p><h3>Closing Thoughts</h3><p>The approach in Kitsune is nice because it allows us to make outfits for arbitrary items using just the seed item’s taxonomy value and a length 50 vector that encodes attribute characteristics. This means it extends to never-before-seen items and can be used in a real world product catalog if we so wished. It was a path I wanted to go down because if we were able to get reasonable looking results with just generating the attribute vectors, it meant that we could mix and match them sort of at will to create a lot of possible outfits.</p><p>One idea that came up when I showed my team this project was that a user could technically input taxonomy categories they were personally interested in rather than just the ones generated by the model. For instance, say we output a dress, high heeled shoes, and a bag, and one user says that they don’t wear high heels, but would like to see wedges as options instead. This Kitsune pipeline supports that type of flexibility and exploration.</p><p>From a technical point of view, it is interesting to me that the model used on the backend is surprisingly simple. It is just a few linear layers piped into three task heads. There is a niceness in the simplicity here since I have thought about a lot of other architectures and setups, all of which involve much heavier weight modeling techniques, but sometimes simple solutions get the job done.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2dabff17c690" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Project Pendragon + Tonks: Multi-Task Feature Extraction for Farming Fate Grand Order]]></title>
            <link>https://medium.com/data-science/project-pendragon-tonks-multi-task-feature-extraction-for-farming-fate-grand-order-af077b7aafd2?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/af077b7aafd2</guid>
            <category><![CDATA[towards-data-science]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Mon, 11 May 2020 02:14:10 GMT</pubDate>
            <atom:updated>2020-05-11T02:14:10.184Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/965/1*wYZec0tPa3dS0qQUS0C8tA.png" /></figure><p>When not blogging about data science, I work as a Senior Data Scientist at an e-commerce company called ShopRunner. Over the past year, our team has been building out large multi-task deep learning ensembles to predict relevant fashion attributes and characteristics of products within our product catalog using both images and text. Recently, our team open-sourced the main training pipelines and framework that we have been building internally to train our multi-task learners in a package called Tonks. Tonks is available to install on <a href="https://pypi.org/project/tonks/">pypi</a>, with the source code available on GitHub <a href="https://github.com/ShopRunner/tonks">here</a>.</p><p>As we went through the process of discussing open sourcing Tonks I realized a potential early use case could be upgrading a side project of mine where I am building a series of reinforcement learning (RL) agents to play the mobile phone game Fate Grand Order (FGO), which I’ve nicknamed Project Pendragon.</p><p>Project Pendragon has two main parts, the feature extraction pipeline which sends inputs to the RL agents that make decisions and send commands back to play the game. While my RL agents that play FGO have been repeatedly upgraded, my feature extraction pipelines are basically where they were at a year ago.</p><p>This post will cover how I replaced my original feature extraction pipeline which used three large convolutional neural networks (two ResNet 50s and a ResNet 34) and replaced these three models with a single Tonks multi-task ResNet 50 trained with multiple datasets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*bt_11ZbRAcDqYNCRlFO_aw.gif" /><figcaption>An early version of multiple RL agents playing through FGO content with multi-task network extracting information from the FGO mobile phone game.</figcaption></figure><h3>Tonks</h3><p>Our ShopRunner team has built out our PyTorch based Tonks library to help us build large multi-task network ensembles using both images and text. In most of those use cases, we care about being able to return relevant attributes about products based on the provided images, descriptions, title, and other information.</p><p>When you build multi-task learners, you are usually attacking a problem with the mindset that features learned on one task might be beneficial to another. For us in the e-commerce space, two tasks like this might be dress length and sleeve length where two single task models would likely learn to look for lines and length while not worrying about color, pattern, and background. When tasks meet these criteria it means we can combine the tasks into multi-task networks and in tanks, we do this by having a core model such as a ResNet for images or a Bert model for text and connect the outputs of these core models to our individual task heads</p><p>When your tasks are all within the same domain multi-task learning is useful because it lets you build and maintain a single model instead of using multiple single task learners. In my current pipeline, I train and use 3 large CNNs where I build tailored datasets for each task previously. Tonks is built to handle situations where you train using tailored datasets for individual tasks so here it helps me cut down on the number of models I need to maintain and lets me train with my previously used datasets.</p><p>Something to think about when building multi-task models is that when tasks are not within similar domains, multi-task models can suffer from <em>destructive interference</em>,<em> </em>where conflicting signals from different tasks pull the model in different directions. A discussion of how to handle this destructive interference could be a good follow- up post or talk, but is beyond the scope of this post. For my FGO use case, my gut feeling was that the problem was fairly doable since all the tasks are all using FGO screenshots, similar-ish text, color palettes, etc., so I would likely not have issues with destructive interference.</p><p>For more details about Tonks see our launch <a href="https://medium.com/shoprunner/tonks-building-one-multi-task-model-to-rule-them-all-3e5d020f1f2b">post</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*VoGIJO5_Pz86FGNDPIY_KA.gif" /><figcaption>First CNN detects the attack button, Python sends a click to it. On the next screen, another CNN detects the 5 command card types and an RL bot makes a decision based on that input.</figcaption></figure><h3>Project Pendragon</h3><p>In the Fall of 2018, I started making some basic bots to play FGO, but before I go into the details of the bots, I’ll give a quick rundown of what FGO even is. Fate Grand Order is a turn-based mobile phone game where you pick between 3 and 6 characters with various stats and abilities. You then use that team to fight through waves of enemies, until all enemies or the team is defeated.</p><p>My main motivation for building these bots is that as part of regular events in FGO, players are often required to farm (repeatedly play) levels dozens, if not hundreds, of times. For a recent event, I did 100 farming runs over a week-long period where each level takes between 3–5 minutes to play (so 5–8 hours of gameplay in total). So with that in mind, I thought building a bot to be able to do this repetitive task for me would be a great side project! This once-simple “side project” has since turned into an interesting on-again-off-again year-long rabbit hole with a lot of interesting additions like <a href="https://towardsdatascience.com/pendragon-four-multi-agent-reinforcement-learning-with-fate-grand-order-80f6254754dd">multiple reinforcement learning agents</a> and various custom game environments.</p><p>Despite making many upgrades to the codebase around the bots and how the decisions get made, I really haven’t touched my feature extraction pipelines to get information to my RL game bots.</p><h4>Feature Extractor One: Whose turn is it now?</h4><p>FGO is a turn-based game, and in order for the bots to play the game, I needed to be able to detect when it was actually their turn. The way that I decided to detect when the bots’ turn was starting was to look for the “attack” button that appears on the main combat screen before command cards are picked.</p><p>Below is a sample main combat screen where the attack button is highlighted in the bottom right corner.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*pFTNLlSZBUwJMgU78n4N9Q.png" /><figcaption>The attack button at the bottom right corner, highlighted.</figcaption></figure><p>I structured this network to have 2 classes, “attack” and “not attack,” basically meaning the network is trained to detect whether or not the attack button is present in that portion of the game screen. If it is, then it means that it is the bots’ turn and you can do useful things like take actions/use skills on the present screen or go ahead and bring up the command card screen where you can pick which of the 5 cards to play.</p><p>Picking command cards is the main combat mechanic of FGO. So once I was able to detect whose turn it was, it was time to build a classifier that would help me identify what command cards had been dealt that turn. My first bots used this information to play algorithmically while later bots were trained via reinforcement learning to choose which cards to play, but all of them needed to be able to identify which cards had been dealt as a key part of the feature extraction process.</p><h4>Feature Extractor Two: What command cards were dealt?</h4><p>The main combat mechanic in FGO is picking “command cards” on your turn. There are 3 types of cards: “Arts”, “Buster”, and “Quick,” and each card type does slightly different things. In each turn, 5 cards are dealt and the player has to pick 3 of them to play that turn. Below is a sample of the 5 cards presented and 3 being picked.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*Xdsc7jDp7z6f5jFy4lRI8Q.gif" /><figcaption>Sample command cards and cards being picked. The feature extraction model predicts the type of each of the 5 cards. Those predictions are then sent to a “card picker” model which decides which 3 to play.</figcaption></figure><p>While I could have built a detector to find the card location behind the scenes, I found an easier solution. These screens are relatively consistent and the cards are placed in the same spot, so what I opted to do is hard code the locations of the five command cards and crop them out of screenshots of the command card screen (see below for an example). Then, I passed each of the five cards through a PyTorch-trained CNN to determine the card “type”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*TgJlDSABq9oQMeAQXKwFtg.jpeg" /><figcaption>Colored sections are the 5 command cards dealt.</figcaption></figure><p>These card type classifications can then be fed into various RL agents and used to make decisions about which cards to play on that given turn. For a long time, these two networks (attack button and card type detector) were the core feature extractors for my first FGO reinforcement bot, nicknamed <a href="https://towardsdatascience.com/project-pendragon-part-2-a-reinforcement-learning-bot-for-fate-grand-order-7bc75c87c4f3">Pendragon Alter</a>. They let me make a bot that could do the main combat in FGO and also play through a full game in an automated way. From this point onward, I really just had to think about what other information I would need to play the game and how to extract that from the game.</p><h4>Feature Extractor Three: What wave of enemies are we on?</h4><p>The final network I added into my framework is actually a wave counter that I use as an input into several different versions of my bots. The reason I added this is that FGO levels are almost always structured as having between 1 and 3 rounds of enemies that you have to fight through, and the actions an agent may want to take may depend on the round. For instance, the first wave of enemies may be relatively weak, but the third wave can be quite strong, so saving abilities for the third wave is often a good tactic.</p><p>I highlighted the round counter in red near the top of the below screenshot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j8YS42A2u9mnE_c5qInIAQ.png" /><figcaption>Round counter shows current round and how many total rounds</figcaption></figure><p>While I could have used some sort of optical character recognition to get the number, all I really cared about is telling whether it is round 1, 2, or 3, so I trained a CNN to have three classes mapping to those numbers and every time the attack button is detected, I check to see what round number it currently is.</p><p>These three networks achieve the basic feature extraction that I need, but needing to run three large CNNs simultaneously adds quite a bit of GPU or CPU computational overhead and additional storage (2 Resnet 50s are ~220MB and 1 Resnet 34 is ~84Mb).</p><h3>Tonks Training Pipeline</h3><p>Our Tonks pipeline follows the general framework setup by Fastai where the pipeline is organized into data loaders, models, and learners. We end up managing the multiple tasks and multiple datasets using a variety of dictionaries which help with bookkeeping.</p><p>The following sections will show code snippets that appear in the linked training notebook below and discuss what is happening in them.</p><blockquote>The notebook I used for training is located <a href="https://github.com/sugi-chan/fgo_tonks_training/blob/master/fgo_tonks.ipynb">here</a></blockquote><h4>Tonks Dataset and Dataloaders</h4><p>Our custom Tonks dataset (line 26), FGOImageDataset, follows fairly standard PyTorch dataset layouts where we need to provide a way to index appropriate values as part of the data generator (lines 58–59), apply transformations (line 61), and return the image data and label (line 65).</p><p>Lines 11–24 get applied depending on whether or not we are looking at the training for validation set. I like keeping the transforms in this format, but it’s just a personal preference.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/596/1*BJhv7PS7AYSydbJnFwHlDw.png" /><figcaption>Cell 14 of the fgo_tonks notebook</figcaption></figure><p>Once we have created our custom Tonks dataset class, we can start creating our training and validation datasets for our three tasks. This part of the process is also fairly similar to a standard PyTorch training pipeline, where you have to place your training and validation splits into datasets and then eventually into dataloaders. The only difference here is that we have three datasets instead of the normal one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1016/1*i6IzHH_UmNcbGMMuu7f2Og.png" /><figcaption>Lines 1–26 show how we prepare train and validation splits for each of the three tasks. Lines 28–37 show how we make a dictionary of dataloders which we use in the next steps to make out Tonks’ multi-task multi-dataset dataloaders.</figcaption></figure><p>In this above section of code, we are creating training and validation datasets for each class using the custom dataset class I showed previously. This involves specifying the x and y inputs (file paths to images and their labels) as well as what transforms we would like to apply. In this pipeline, I just have randomized crops in the training set with ImageNet normalization and only normalization in the validation sets. Once the datasets are created we make a dictionary of base PyTorch dataloaders where the keys are the names of the tasks and the values are the dataloaders associated with those tasks. The idea here is that we can keep track of which dataset we should generate batches for as part of our multi-dataset training pipeline.</p><p>The below snippet shows how we place our two dictionaries of PyTorch dataloaders into two Tonks MultiDatasetLoaders. These Tonks dataloaders are what we use to integrate with our multi-task multi-dataset training pipeline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/776/1*17TQIjPLlAR3p30xopcZpw.png" /><figcaption>Tonks MultiDatasetLoaders</figcaption></figure><h4>Example Tonks Network Architecture</h4><p>The next major piece of the pipeline is the model. While we provide some sample image and text model architectures, it might make sense to customize the architectures to your needs. For me, I just made a simple ResNet50-based architecture and connected that to the individual task layers. In Tonks, we handle this part with two PyTorch ModuleDicts called the pretrained_classifiers and new_classifiers . The idea we had here is that the first time you train a network we send tasks to the new_classifiers dictionary and when we save the trained network these get moved to the pretrained_classifier dictionary for saving. Then on subsequent runs, we can load pretrained task heads into the pretrained_classifier dictionary. This helps us with keeping track of where to apply learning rates (since you might want to have different learning rates depending on whether or not a task head has previously been fine tuned or not)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/719/1*AxKyxXRpyXAqU_EWYbVbDg.png" /></figure><h4>Loading Tonks Model</h4><p>The main inputs when we are loading an instance of a Tonks model is in the task_dictionary. There are two potential ones: the first is a new_task_dict, which is the one you should use the first time you are training a model, while the second is a pretrained_task_dict, which is where you would place tasks where there are already existing Tonks pre-trained weights for those tasks. For us at ShopRunner, this is useful because we can now potentially add on new tasks to existing models with ease.</p><p>For this FGO example, I have three tasks that are all new. I will place a dictionary with the task name and the number of categories in each task into a dictionary called new_task_dict and feed that into the model class when I initialize it. This is what tells the Tonks model to create three task heads with a certain number of nodes in each.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/728/1*u4GiAp0iLe7qI5CFKGHkLg.png" /></figure><h4>Training</h4><p>Once we have our model initialized, all we need to do before we kick off our training run is define a loss function for the various tasks, specify an optimizer, assign learning rates, create our learner, and call the fit function.</p><p>For this pipeline, each of the tasks is a multi-class problem, so we use a cross entropy loss. When we assign learning rates, we may assign different learning rates to different sections of the model. For the main ResNet section, we assign a low 1e-4 learning rate, but for the new sections, we assign a more aggressive 1e-2 learning rate.The main idea behind this is that we don’t really want to drastically change the ImageNet weights in the ResNet50 core model, but since the new classifier layers are randomly initialized, we can let them be adjusted more aggressively. Then we define a scheduler to decrease the learning rate every 2 epochs.</p><p>Once that is done, we can define our learner using the Tonks MultiTaskLearner class. This class contains all the functionality we need to train our models and takes in the model architecture we loaded previously, the training and validation Tonks dataloaders, and the task dictionary, which contains a mapping of all our tasks which is used to retrieve batches from our dataloaders.</p><p>Finally, we can call fit() on our learner. For details on the different arguments you can check our <a href="https://tonks.readthedocs.io/en/latest/">read the docs</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/932/1*vN4wF_CGqYlfAYN7KRQr7g.png" /></figure><h3>Results</h3><p>Once I finished training the three-task Tonks network, I just had to replace the three networks in my Project Pendragon repos with the new multi-task network. Since Tonks is built on PyTorch, in order to use a model, all you need to keep track of is the model architecture and the weights file, so you don’t necessarily need to install Tonks and all of its dependencies to use the trained models in new projects.</p><p>The Tonks model performs strongly across all tasks and so far I have not had any issues in deployment in my FGO pendragon game interface. I have been using the Tonks model for my other recent developments as I continue to improve my RL agents. The <a href="https://towardsdatascience.com/breaking-the-game-pendragon-four-rise-of-merlin-4d9acfe5fbd7">most recent installment</a> is getting the agents to play using coordinated strategies.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*1cABXfZOSPMpolQPjPHvIw.gif" /><figcaption>Agents coordinating to clear difficult content. Feature extraction for the agents and bots are based on the multi-task tonks model trained here.</figcaption></figure><p>So while this example is a cute one based on a mobile phone game, the reasons to use a framework like Tonks are the same here as they are when we look at industrial scale problems. It removed my need to maintain multiple single task learner networks and I was able to train quickly and easily because I was able to use three existing datasets I had previously built.</p><p>Tonks is a library that our ShopRunner Data Science team has been using to build industrial scale multi-task deep learning ensembles using both images and text trained with multiple datasets. For us, it has made it relatively straightforward for our team to create multi-task models to meet new and varying needs. Since building multi-task learners is a very real world need, but one that is not currently supported we open-sourced our work to help give back to the data science community.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af077b7aafd2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/project-pendragon-tonks-multi-task-feature-extraction-for-farming-fate-grand-order-af077b7aafd2">Project Pendragon + Tonks: Multi-Task Feature Extraction for Farming Fate Grand Order</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tonks: Building One (Multi-Task) Model to Rule Them All!]]></title>
            <link>https://medium.com/@michaelsugimura/tonks-building-one-multi-task-model-to-rule-them-all-3e5d020f1f2b?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/3e5d020f1f2b</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[collaboration]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Tue, 28 Apr 2020 16:01:38 GMT</pubDate>
            <atom:updated>2020-08-12T14:26:23.627Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Hexagonal icon that says Tonks and has five different hairstyles pictured all in neon colors." src="https://cdn-images-1.medium.com/max/1024/0*VxGc2D7kfoA_t7EY" /></figure><h4>Co-written by <a href="https://medium.com/@parsing_science">Nicole Carlson</a> and <a href="https://medium.com/@michaelsugimura">Michael Sugimura</a></h4><p><em>NOTE: Our team previously had a tradition of naming projects with terms or characters from the Harry Potter series, but we are disappointed by J.K. Rowling’s persistent transphobic comments. In response, we renamed the Tonks Library as Octopod. More details on that process </em><a href="https://medium.com/shoprunner/whats-in-a-name-sr-data-science-python-libraries-2-0-2e87ba89a2da"><em>here</em></a><em>.</em></p><h3>Intro</h3><p><a href="https://twitter.com/parsing_science">Nicole Carlson</a> and <a href="https://medium.com/@michaelsugimura">Michael Sugimura</a> are the lead developers on Tonks, a multi-task deep learning library (<a href="https://pypi.org/project/tonks/">pypi</a>, <a href="https://github.com/ShopRunner/tonks">github</a>). This post is the story of how we built this library together. We will discuss technical details of the library as well as interpersonal challenges we faced along the way. This project ended up being a rich experience for us, in ways we never could have guessed.</p><h3>What is Tonks?</h3><p>Tonks is a library that streamlines the training of multi-task PyTorch networks. It supports training with multiple task-specific datasets, multiple inputs, and ensembles of multi-task networks.</p><p>We started building Tonks to meet our need to build multi-task networks at scale. At ShopRunner, we have millions of products aggregated from 100+ retailers. In order to facilitate better browsing and search, we need to label attributes such as color and pattern for each product.</p><p>We first considered building individual task networks for each task, e.g. a color neural network, a pattern neural network, a season neural network, etc. However, we quickly realized that maintaining that many models would be difficult.</p><p>We decided to build one multi-task model that could predict all of our attributes using both images and text. In our fashion domain, leveraging both images and text of products boosts the performance of our models, so we had to be able to ensemble image and text models together. To meet all of these criteria, we made a library, Tonks, to use as a training framework for the multi-task, multi-input models we use in production.</p><p>One issue that often arises with multi-task networks is that other libraries require you to have one dataset with every attribute labeled. Our library allows you to train each task with a different dataset in the same neural network. For example, you can train one network to predict both pants length and dress length from two separate labeled datasets of pants and dresses.</p><p>In our initial release of Tonks, we are open sourcing our pipelines, data loaders, and some sample model classes. We’ve also created tutorials for training image models, text models, and ensembles of image and text models. Now we’ll tell you how we actually built Tonks.</p><h3>Initial R&amp;D</h3><p><strong>Michael</strong>: The core functionality of Tonks is building multi-task models using the PyTorch deep learning framework, but one of the major problems we had to solve is how to train multi-task network with multiple datasets simultaneously. Unlike multi-task training, multi-dataset training is something that is talked about less since it is a less common research use case, but does make sense for industry applications.</p><p>Developing this multi-dataset multi-task pipeline took a good bit of R&amp;D and during that time I took inspiration from Stanford Dawn and their <a href="https://dawn.cs.stanford.edu/2019/03/22/glue/">blog</a> about training multi-task NLP models and relistened to <a href="https://www.youtube.com/watch?v=UdXfsAr4Gjw">Andrew Ng</a> discussing it in his 2017 deep learning course more than a few times while I was stuck in research mode. However after a lot of trial and error, I was able to get a methodology for multi-dataset multi-task training working:</p><ol><li>Prepare all of your datasets and place them into data loaders (PyTorch data generators).</li><li>Randomly shuffle the batches of the various datasets.</li><li>Sample randomly without replacement and feed that batch through our deep learning model.</li><li>Calculate the loss for that specific batch based on the outputs from that specific batch’s matching task head. For example, if a batch from a pattern dataset is selected, we only calculate the loss based on the pattern task’s output and use that for backpropagation. At every batch the model is predicting all outputs, but is only rewarded/punished for its decisions on the relevant task.</li></ol><p>An epoch consists of repeating steps 3 and 4 until all batches have been sampled.</p><p>See below for a graphic walkthrough of this process:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*gcmall5_nQ0AnQGOTK6YPA.gif" /><figcaption>Graphic showing multi-dataset multi-task training pipeline.</figcaption></figure><p>After working out this part of the process, the code was really just in rough notebook form. Around this time Nicole ended up finishing up on another workstream and was able to assist me in turning the notebooks into production ready code!</p><h3>Refactoring the Code</h3><p><strong>Nicole</strong>: Michael’s first version of our attribute multi-task model used only images. We always knew we wanted to add in the text, but he was busy with other work. Since I was interested in NLP, I offered to work on adding text to our attribute model.</p><p>As Michael mentioned above, his work was mostly in Jupyter notebooks. As I started digging through his code, the software engineer in me couldn’t help but want to get rid of repeated code that was copied into multiple notebooks. I also knew that if I broke the code out into functions and classes that it would help me understand what was happening.</p><p>I ended up completely refactoring the code from Michael’s notebooks into a python library. I moved all of the training/evaluation code into a learner class. I also created custom dataloaders that did the necessary preprocessing for our models. Once I had the library refactored, it was pretty straightforward to add in a text component to the original attribute model architecture.</p><p>I initially refactored the code for my own understanding, but I decided to share it with Michael to see if he thought it would be useful to add in to Tonks. Unfortunately, this refactoring was completely outside the scope of my ticket. I was supposed to just add in the text component, but I had rewritten the entire library. And I had not communicated that I was doing this to Michael. I definitely regret the way that I handled this; I should have communicated with Michael much earlier in the project.</p><figure><img alt="Screenshot of Github Pull Request (PR) with the title “DS 1420 train dress model”. The PR has 74 commits and 33 changed files" src="https://cdn-images-1.medium.com/max/1024/0*ddU77z_-g9XIxLFT" /><figcaption>Github Pull Request for the PR where I (Nicole) refactored everything. As you can see by the initial name, I was only supposed to train a new model, not change all 33 files in the library!</figcaption></figure><p><strong>Michael</strong>: With our very different styles, it took Nicole and I a bit to hit our groove for working as a team. So when Nicole started the refactoring process, my first knee-jerk reaction was being slightly defensive since it was outside of the current scope of work. After sitting back and thinking about it for a bit, I realized that Nicole’s refactoring work was better for Tonks as a project overall since Nicole is a very talented engineer and was helping to get Tonks to a productionalized state. While I knew this was good for Tonks, I felt like I was being dead weight for this part of the process because I felt like I wasn’t actively contributing or helping Nicole with refactoring. This was a breakdown in communication on my part because I could have both helped get the code to an initially stronger state and have tried to engage more along the way to help Nicole out with the whole process.</p><figure><img alt="Michael and Nicole shaking fists towards each other in mock anger from two panels of a zoom call." src="https://cdn-images-1.medium.com/max/942/0*8yU2VZLOALryjHnF" /><figcaption>Michael and Nicole shaking fists towards each other from two panels of a zoom call.</figcaption></figure><p>When I finally got to see and use Nicole’s refactored pipelines, my personal feeling was that it was like seeing fire for the first time. It was elegant, simple but complex, and very powerful. From my point of view, my hacky R&amp;D gave our Tonks project an overall shape, but Nicole’s refactoring is what gave it a heart and soul.</p><p><strong>Nicole</strong>: The ironic thing about the refactor was that I was much more impressed with Michael’s work than my own. When I looked at Michael’s code, I was completely blown away by the work he had done. Randomly sampling through the different tasks during training was so elegant, and I knew I could never have come up with that by myself. I was also relatively new to PyTorch so I was amazed at how easily Michael had built a model architecture that could use both images and text. I felt like I wasn’t really contributing much to the project since I had only refactored some code, not done any of the R&amp;D work.</p><p>We actually each separately went to our boss with our fears that we weren’t contributing to this project. She encouraged us to talk directly and express how much we appreciated the other’s work. This was another lesson for us about being more communicative with one another and valuing the fact that we each brought different strengths and weaknesses to the project.</p><h3>Adding a New Attribute</h3><figure><img alt="Network diagram showing an architecture where all tasks were built from single image and text models and ensembled" src="https://cdn-images-1.medium.com/max/1024/0*UDcw5XZ3ltByZnUr" /><figcaption>Example of an original attribute model architecture with two attributes: Color and Season.</figcaption></figure><p><strong>Nicole</strong>: Our original attribute model had four attributes. After refactoring the model, we were confident that it would be relatively straightforward for us to add in a fifth attribute. Unfortunately, that was not the case. As soon as I added in the fifth attribute, the performance of one of the other attributes would degrade. I could not get high performance across all five attributes. I trained single task models for each model to get baselines for each task, but the multi-task model could not get close. So I went to Michael for help.</p><p><strong>Michael</strong>: This whole thing was both very interesting and also terrifying, since most multi-task literature just discusses how networks improve with additional tasks that fall within the same domain. Much like detective work, we really needed a clue to help get us to a breakthrough. For this our breakthrough came from that same Stanford <a href="https://dawn.cs.stanford.edu/2019/03/22/glue/">blog</a>, the same one I had initially used as inspiration for our Tonks pipeline. They mentioned a problem with something called “destructive interference” with tasks and how they dealt with it for NLP competition leaderboard purposes. Looking into “destructive interference”, I found that it is a problem in multi-task networks where unrelated or weakly related tasks can pull a network in opposing directions when trying to optimize the weights. For that bit of research, this paper section 3.1 was helpful.</p><p>So the symptoms that we were seeing in our multi-task models matched literature around destructive interference. Now that we know our foe, all Nicole and I had to do was figure out a way to best it.</p><h3>Multiple ResNets</h3><figure><img alt="Network diagram where separate image networks’ features are ensembled with a BERT model for final predictions." src="https://cdn-images-1.medium.com/max/1024/0*Ppumgfmv_M2lZdwG" /><figcaption>Example of a multiple ResNet ensemble model where tasks are separated if they cause destructive interference.</figcaption></figure><p><strong>Nicole</strong>: After Michael discovered the destructive interference, we realized that our best solution was to have multiple ResNets in our final ensemble model. I modified the ensemble model class to accommodate this new architecture, and we finally had a model that retained high performance with new tasks.</p><p>This problem also drove home the lesson that Michael and I were much stronger as a team. Michael did the research to name and solve our problem, and I modified our library to incorporate the solution.</p><h3>Conclusion</h3><figure><img alt="Michael and Nicole pointing finger guns at each other and smiling from two Zoom panels." src="https://cdn-images-1.medium.com/max/1024/0*nTBD75yUW8DqHXO6" /><figcaption>Michael and Nicole pointing finger guns at each other from two Zoom panels.</figcaption></figure><p>We hope that deep learners everywhere will enjoy using our library. This was a great learning experience for us, and it really proved that having people with opposite strengths work together was more powerful than either of us working alone. Although we had some communication issues along the way, we’ve come out of this with a much stronger working relationship. Even as we wrote this blog post, we realized we were repeating the same pattern: Michael wrote the initial draft and Nicole edited the text. This time around, we communicated with one another before making changes to the other person’s work!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3e5d020f1f2b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Breaking the Game: Pendragon Four Rise of Merlin]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/breaking-the-game-pendragon-four-rise-of-merlin-4d9acfe5fbd7?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/600/0*sYQJnIr1G_UGvMGE" width="600"></a></p><p class="medium-feed-snippet">Multi-agent reinforcement learning for the mobile phone game Fate Grand Order adding in powerful supports to clear late-game content</p><p class="medium-feed-link"><a href="https://medium.com/data-science/breaking-the-game-pendragon-four-rise-of-merlin-4d9acfe5fbd7?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/breaking-the-game-pendragon-four-rise-of-merlin-4d9acfe5fbd7?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/4d9acfe5fbd7</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[reinforcement-learning]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[videogames]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Tue, 25 Feb 2020 14:44:19 GMT</pubDate>
            <atom:updated>2020-02-25T14:44:19.884Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Pendragon Four: Training Pipeline Deeper Dive for Multi Agent Reinforcement Learning]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/pendragon-four-training-pipeline-deeper-dive-for-multi-agent-reinforcement-learning-80df2434dd0?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/2600/1*13aoEJgD3J9dzEZsnBemQg.png" width="2708"></a></p><p class="medium-feed-snippet">Deeper dive into training multiple RL agents simultaneously to play the mobile phone game Fate Grand Order</p><p class="medium-feed-link"><a href="https://medium.com/data-science/pendragon-four-training-pipeline-deeper-dive-for-multi-agent-reinforcement-learning-80df2434dd0?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/pendragon-four-training-pipeline-deeper-dive-for-multi-agent-reinforcement-learning-80df2434dd0?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/80df2434dd0</guid>
            <category><![CDATA[deep-lear]]></category>
            <category><![CDATA[videogames]]></category>
            <category><![CDATA[fate-grand-order]]></category>
            <category><![CDATA[reinforce]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Mon, 17 Feb 2020 14:50:03 GMT</pubDate>
            <atom:updated>2020-02-17T14:50:03.803Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Pendragon Four: Multi-Agent Reinforcement Learning with Fate Grand Order]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/pendragon-four-multi-agent-reinforcement-learning-with-fate-grand-order-80f6254754dd?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/2084/1*igK--vCoAbj1nL7QIw25Hw.png" width="2084"></a></p><p class="medium-feed-snippet">Multi-agent reinforcement learning in a custom game environment to train 4 agents and have them play the mobile phone game Fate Grand Order</p><p class="medium-feed-link"><a href="https://medium.com/data-science/pendragon-four-multi-agent-reinforcement-learning-with-fate-grand-order-80f6254754dd?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/pendragon-four-multi-agent-reinforcement-learning-with-fate-grand-order-80f6254754dd?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/80f6254754dd</guid>
            <category><![CDATA[fate-grand-order]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[reinforcement-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Tue, 17 Dec 2019 15:32:07 GMT</pubDate>
            <atom:updated>2019-12-17T15:32:07.436Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[This Dress Doesn’t Exist]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/this-dress-doesnt-exist-90002800f683?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/600/1*eQ9tsqvCX8bkk2Qw-Kcf4g.gif" width="600"></a></p><p class="medium-feed-snippet">Fine tuning GPT-2 and StyleGAN for a fashion use case to generate synthetic product images and descriptions</p><p class="medium-feed-link"><a href="https://medium.com/data-science/this-dress-doesnt-exist-90002800f683?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/this-dress-doesnt-exist-90002800f683?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/90002800f683</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[fashion]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Wed, 09 Oct 2019 17:23:02 GMT</pubDate>
            <atom:updated>2019-10-09T17:23:02.321Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Building and Labeling Image Datasets for Data Science Projects]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/building-and-labeling-image-datasets-for-data-science-projects-ab59172e46b4?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/2600/1*S7k6xkltoxQr9btwJlptXQ.png" width="2832"></a></p><p class="medium-feed-snippet">Some tips and tricks for building image datasets</p><p class="medium-feed-link"><a href="https://medium.com/data-science/building-and-labeling-image-datasets-for-data-science-projects-ab59172e46b4?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/building-and-labeling-image-datasets-for-data-science-projects-ab59172e46b4?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ab59172e46b4</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Mon, 09 Sep 2019 11:30:43 GMT</pubDate>
            <atom:updated>2019-09-09T13:20:13.514Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[BERT Classifier: Just Another Pytorch Model]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/bert-classifier-just-another-pytorch-model-881b3cf05784?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/2600/1*wgzXx_W_Njh4g3kXHQEN6A.jpeg" width="3872"></a></p><p class="medium-feed-snippet">Building a custom Pytorch pipeline for a BERT classifier to figure out how BERT works piece by piece</p><p class="medium-feed-link"><a href="https://medium.com/data-science/bert-classifier-just-another-pytorch-model-881b3cf05784?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/bert-classifier-just-another-pytorch-model-881b3cf05784?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/881b3cf05784</guid>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Mon, 10 Jun 2019 13:53:47 GMT</pubDate>
            <atom:updated>2019-06-10T14:18:20.873Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Two Tasks, Two Datasets, One Network: Multi-task Learning with DnD]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/data-science/two-tasks-two-datasets-one-network-multi-task-learning-with-dnd-eaef00b5e741?source=rss-5d6bed290a7c------2"><img src="https://cdn-images-1.medium.com/max/1920/1*SGUj5UzJ0ayVHjFKBOX68Q.jpeg" width="1920"></a></p><p class="medium-feed-snippet">Multi-task learning with multiple datasets to learn multiple tasks.</p><p class="medium-feed-link"><a href="https://medium.com/data-science/two-tasks-two-datasets-one-network-multi-task-learning-with-dnd-eaef00b5e741?source=rss-5d6bed290a7c------2">Continue reading on TDS Archive »</a></p></div>]]></description>
            <link>https://medium.com/data-science/two-tasks-two-datasets-one-network-multi-task-learning-with-dnd-eaef00b5e741?source=rss-5d6bed290a7c------2</link>
            <guid isPermaLink="false">https://medium.com/p/eaef00b5e741</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Michael Sugimura]]></dc:creator>
            <pubDate>Mon, 03 Jun 2019 12:45:37 GMT</pubDate>
            <atom:updated>2019-06-03T13:30:16.928Z</atom:updated>
        </item>
    </channel>
</rss>