├── .gitignore ├── LICENSE ├── README.md ├── README_ko-KR.md ├── README_zh-tw.md ├── data ├── dolphins-and-seahorses │ ├── dolphin │ │ ├── image_0001.jpg │ │ ├── image_0002.jpg │ │ ├── image_0003.jpg │ │ ├── image_0004.jpg │ │ ├── image_0005.jpg │ │ ├── image_0006.jpg │ │ ├── image_0007.jpg │ │ ├── image_0008.jpg │ │ ├── image_0009.jpg │ │ ├── image_0010.jpg │ │ ├── image_0011.jpg │ │ ├── image_0012.jpg │ │ ├── image_0013.jpg │ │ ├── image_0014.jpg │ │ ├── image_0015.jpg │ │ ├── image_0016.jpg │ │ ├── image_0017.jpg │ │ ├── image_0018.jpg │ │ ├── image_0019.jpg │ │ ├── image_0020.jpg │ │ ├── image_0021.jpg │ │ ├── image_0022.jpg │ │ ├── image_0023.jpg │ │ ├── image_0024.jpg │ │ ├── image_0025.jpg │ │ ├── image_0026.jpg │ │ ├── image_0027.jpg │ │ ├── image_0028.jpg │ │ ├── image_0029.jpg │ │ ├── image_0030.jpg │ │ ├── image_0031.jpg │ │ ├── image_0032.jpg │ │ ├── image_0033.jpg │ │ ├── image_0034.jpg │ │ ├── image_0035.jpg │ │ ├── image_0036.jpg │ │ ├── image_0037.jpg │ │ ├── image_0038.jpg │ │ ├── image_0039.jpg │ │ ├── image_0040.jpg │ │ ├── image_0041.jpg │ │ ├── image_0042.jpg │ │ ├── image_0043.jpg │ │ ├── image_0044.jpg │ │ ├── image_0045.jpg │ │ ├── image_0046.jpg │ │ ├── image_0047.jpg │ │ ├── image_0048.jpg │ │ ├── image_0049.jpg │ │ ├── image_0050.jpg │ │ ├── image_0051.jpg │ │ ├── image_0052.jpg │ │ ├── image_0053.jpg │ │ ├── image_0054.jpg │ │ ├── image_0055.jpg │ │ ├── image_0056.jpg │ │ ├── image_0057.jpg │ │ ├── image_0058.jpg │ │ ├── image_0059.jpg │ │ ├── image_0060.jpg │ │ ├── image_0061.jpg │ │ ├── image_0062.jpg │ │ ├── image_0063.jpg │ │ ├── image_0064.jpg │ │ └── image_0065.jpg │ └── seahorse │ │ ├── image_0001.jpg │ │ ├── image_0002.jpg │ │ ├── image_0003.jpg │ │ ├── image_0004.jpg │ │ ├── image_0005.jpg │ │ ├── image_0006.jpg │ │ ├── image_0007.jpg │ │ ├── image_0008.jpg │ │ ├── image_0009.jpg │ │ ├── image_0010.jpg │ │ ├── image_0011.jpg │ │ ├── image_0012.jpg │ │ ├── image_0013.jpg │ │ ├── image_0014.jpg │ │ ├── image_0015.jpg │ │ ├── image_0016.jpg │ │ ├── image_0017.jpg │ │ ├── image_0018.jpg │ │ ├── image_0019.jpg │ │ ├── image_0020.jpg │ │ ├── image_0021.jpg │ │ ├── image_0022.jpg │ │ ├── image_0023.jpg │ │ ├── image_0024.jpg │ │ ├── image_0025.jpg │ │ ├── image_0026.jpg │ │ ├── image_0027.jpg │ │ ├── image_0028.jpg │ │ ├── image_0029.jpg │ │ ├── image_0030.jpg │ │ ├── image_0031.jpg │ │ ├── image_0032.jpg │ │ ├── image_0033.jpg │ │ ├── image_0034.jpg │ │ ├── image_0035.jpg │ │ ├── image_0036.jpg │ │ ├── image_0037.jpg │ │ ├── image_0038.jpg │ │ ├── image_0039.jpg │ │ ├── image_0040.jpg │ │ ├── image_0041.jpg │ │ ├── image_0042.jpg │ │ ├── image_0043.jpg │ │ ├── image_0044.jpg │ │ ├── image_0045.jpg │ │ ├── image_0046.jpg │ │ ├── image_0047.jpg │ │ ├── image_0048.jpg │ │ ├── image_0049.jpg │ │ ├── image_0050.jpg │ │ ├── image_0051.jpg │ │ ├── image_0052.jpg │ │ ├── image_0053.jpg │ │ ├── image_0054.jpg │ │ ├── image_0055.jpg │ │ ├── image_0056.jpg │ │ └── image_0057.jpg └── untrained-samples │ ├── dolphin1.jpg │ ├── dolphin2.jpg │ ├── dolphin3.jpg │ ├── list.txt │ ├── seahorse1.jpg │ ├── seahorse2.jpg │ └── seahorse3.jpg ├── images ├── create-classification-model.png ├── create-new-dataset.png ├── explore-dataset.png ├── load-pretrained-model.png ├── model-attempt1-classify1.png ├── model-attempt1-classify2.png ├── model-attempt1-classify3.png ├── model-attempt1.png ├── model-attempt2-classify1.png ├── model-attempt2-classify2.png ├── model-attempt2-classify3.png ├── model-attempt2-classify4.png ├── model-attempt2.png ├── model-attempt3-classify1.png ├── model-attempt3-classify2.png ├── model-attempt3-classify3.png ├── model-attempt3.png ├── new-image-classification-dataset.png ├── new-image-classification-model-attempt1.png ├── new-image-classification-model-attempt2.png ├── new-image-classification-model-attempt3.png ├── new-image-classification-model.png ├── trained-models.png └── upload-pretrained-model.png └── src ├── alexnet-customized.prototxt ├── classify-samples.py └── googlenet-customized.prototxt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 David Humphrey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | ------------------------------- 24 | 25 | Dolphin and Seahorse images in data/dolphins-and-seahorses from Caltech101 26 | http://www.vision.caltech.edu/Image_Datasets/Caltech101/ 27 | 28 | L. Fei-Fei, R. Fergus and P. Perona. Learning generative visual models 29 | from few training examples: an incremental Bayesian approach tested on 30 | 101 object categories. IEEE. CVPR 2004, Workshop on Generative-Model 31 | Based Vision. 2004 32 | 33 | ------------------------------- 34 | 35 | Dolphin and Seahorse images in data/untrained-samples from various: 36 | 37 | https://unsplash.com/search/dolphin?photo=d93dJ4kehUg 38 | https://unsplash.com/search/dolphin?photo=i5FsBOLsB50 39 | https://unsplash.com/search/dolphin?photo=5tGiaWu1QQs 40 | https://upload.wikimedia.org/wikipedia/commons/d/dd/Black_Sea_fauna_Seahorse.JPG 41 | http://www.fusedjaw.com/wp-content/uploads/2003/10/Brazilian-Seahorse-In-Aquarium.jpg 42 | https://cdn.pixabay.com/photo/2016/08/03/22/30/seahorse-1568021_640.jpg 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Have Fun with Machine Learning: A Guide for Beginners 2 | Also available in [Chinese (Traditional)](README_zh-tw.md). 3 | Also available in [Korean](README_ko-KR.md). 4 | 5 | ## Preface 6 | 7 | This is a **hands-on guide** to machine learning for programmers with *no background* in 8 | AI. Using a neural network doesn’t require a PhD, and you don’t need to be the person who 9 | makes the next breakthrough in AI in order to *use* what exists today. What we have now 10 | is already breathtaking, and highly usable. I believe that more of us need to play with 11 | this stuff like we would any other open source technology, instead of treating it like a 12 | research topic. 13 | 14 | In this guide our goal will be to write a program that uses machine learning to predict, with a 15 | high degree of certainty, whether the images in [data/untrained-samples](data/untrained-samples) 16 | are of **dolphins** or **seahorses** using only the images themselves, and without 17 | having seen them before. Here are two example images we'll use: 18 | 19 | ![A dolphin](data/untrained-samples/dolphin1.jpg?raw=true "Dolphin") 20 | ![A seahorse](data/untrained-samples/seahorse1.jpg?raw=true "Seahorse") 21 | 22 | To do that we’re going to train and use a [Convolutional Neural Network (CNN)](https://en.wikipedia.org/wiki/Convolutional_neural_network). 23 | We’re going to approach this from the point of view of a practitioner vs. 24 | from first principles. There is so much excitement about AI right now, 25 | but much of what’s being written feels like being taught to do 26 | tricks on your bike by a physics professor at a chalkboard instead 27 | of your friends in the park. 28 | 29 | I’ve decided to write this on Github vs. as a blog post 30 | because I’m sure that some of what I’ve written below is misleading, naive, or 31 | just plain wrong. I’m still learning myself, and I’ve found the lack of solid 32 | beginner documentation an obstacle. If you see me making a mistake or missing 33 | important details, please send a pull request. 34 | 35 | With all of that out the way, let me show you how to do some tricks on your bike! 36 | 37 | ## Overview 38 | 39 | Here’s what we’re going to explore: 40 | 41 | * Setup and use existing, open source machine learning technologies, specifically [Caffe](http://caffe.berkeleyvision.org/) and [DIGITS](https://developer.nvidia.com/digits) 42 | * Create a dataset of images 43 | * Train a neural network from scratch 44 | * Test our neural network on images it has never seen before 45 | * Improve our neural network’s accuracy by fine tuning existing neural networks (AlexNet and GoogLeNet) 46 | * Deploy and use our neural network 47 | 48 | This guide won’t teach you how neural networks are designed, cover much theory, 49 | or use a single mathematical expression. I don’t pretend to understand most of 50 | what I’m going to show you. Instead, we’re going to use existing things in 51 | interesting ways to solve a hard problem. 52 | 53 | > Q: "I know you said we won’t talk about the theory of neural networks, but I’m 54 | > feeling like I’d at least like an overview before we get going. Where should I start?" 55 | 56 | There are literally hundreds of introductions to this, from short posts to full 57 | online courses. Depending on how you like to learn, here are three options 58 | for a good starting point: 59 | 60 | * This fantastic [blog post](https://jalammar.github.io/visual-interactive-guide-basics-neural-networks/) by J Alammar, 61 | which introduces the concepts of neural networks using intuitive examples. 62 | * Similarly, [this video](https://www.youtube.com/watch?v=FmpDIaiMIeA) introduction by [Brandon Rohrer](https://www.youtube.com/channel/UCsBKTrp45lTfHa_p49I2AEQ) is a really good intro to 63 | Convolutional Neural Networks like we'll be using 64 | * If you’d rather have a bit more theory, I’d recommend [this online book](http://neuralnetworksanddeeplearning.com/chap1.html) by [Michael Nielsen](http://michaelnielsen.org/). 65 | 66 | ## Setup 67 | 68 | Installing the software we'll use (Caffe and DIGITS) can be frustrating, depending on your platform 69 | and OS version. By far the easiest way to do it is using Docker. Below we examine how to do it with Docker, 70 | as well as how to do it natively. 71 | 72 | ### Option 1a: Installing Caffe Natively 73 | 74 | First, we’re going to be using the [Caffe deep learning framework](http://caffe.berkeleyvision.org/) 75 | from the Berkely Vision and Learning Center (BSD licensed). 76 | 77 | > Q: “Wait a minute, why Caffe? Why not use something like TensorFlow, 78 | > which everyone is talking about these days…” 79 | 80 | There are a lot of great choices available, and you should look at all the 81 | options. [TensorFlow](https://www.tensorflow.org/) is great, and you should 82 | play with it. However, I’m using Caffe for a number of reasons: 83 | 84 | * It’s tailormade for computer vision problems 85 | * It has support for C++, Python, (with [node.js support](https://github.com/silklabs/node-caffe) coming) 86 | * It’s fast and stable 87 | 88 | But the **number one reason** I’m using Caffe is that you **don’t need to write any code** to work 89 | with it. You can do everything declaratively (Caffe uses structured text files to define the 90 | network architecture) and using command-line tools. Also, you can use some nice front-ends for Caffe to make 91 | training and validating your network a lot easier. We’ll be using 92 | [nVidia’s DIGITS](https://developer.nvidia.com/digits) tool below for just this purpose. 93 | 94 | Caffe can be a bit of work to get installed. There are [installation instructions](http://caffe.berkeleyvision.org/installation.html) 95 | for various platforms, including some prebuilt Docker or AWS configurations. 96 | 97 | **NOTE:** when making my walkthrough, I used the following non-released version of Caffe from their Github repo: 98 | https://github.com/BVLC/caffe/commit/5a201dd960840c319cefd9fa9e2a40d2c76ddd73 99 | 100 | On a Mac it can be frustrating to get working, with version issues halting 101 | your progress at various steps in the build. It took me a couple of days 102 | of trial and error. There are a dozen guides I followed, each with slightly 103 | different problems. In the end I found [this one](https://gist.github.com/doctorpangloss/f8463bddce2a91b949639522ea1dcbe4) to be the closest. 104 | I’d also recommend [this post](https://eddiesmo.wordpress.com/2016/12/20/how-to-set-up-caffe-environment-and-pycaffe-on-os-x-10-12-sierra/), 105 | which is quite recent and links to many of the same discussions I saw. 106 | 107 | Getting Caffe installed is by far the hardest thing we'll do, which is pretty 108 | neat, since you’d assume the AI aspects would be harder! Don’t give up if you have 109 | issues, it’s worth the pain. If I was doing this again, I’d probably use an Ubuntu VM 110 | instead of trying to do it on Mac directly. There's also a [Caffe Users](https://groups.google.com/forum/#!forum/caffe-users) group, if you need answers. 111 | 112 | > Q: “Do I need powerful hardware to train a neural network? What if I don’t have 113 | > access to fancy GPUs?” 114 | 115 | It’s true, deep neural networks require a lot of computing power and energy to 116 | train...if you’re training them from scratch and using massive datasets. 117 | We aren’t going to do that. The secret is to use a pretrained network that someone 118 | else has already invested hundreds of hours of compute time training, and then to fine 119 | tune it to your particular dataset. We’ll look at how to do this below, but suffice 120 | it to say that everything I’m going to show you, I’m doing on a year old MacBook 121 | Pro without a fancy GPU. 122 | 123 | As an aside, because I have an integrated Intel graphics card vs. an nVidia GPU, 124 | I decided to use the [OpenCL Caffe branch](https://github.com/BVLC/caffe/tree/opencl), 125 | and it’s worked great on my laptop. 126 | 127 | When you’re done installing Caffe, you should have, or be able to do all of the following: 128 | 129 | * A directory that contains your built caffe. If you did this in the standard way, 130 | there will be a `build/` dir which contains everything you need to run caffe, 131 | the Python bindings, etc. The parent dir that contains `build/` will be your 132 | `CAFFE_ROOT` (we’ll need this later). 133 | * Running `make test && make runtest` should pass 134 | * After installing all the Python deps (doing `pip install -r requirements.txt` in `python/`), 135 | running `make pycaffe && make pytest` should pass 136 | * You should also run `make distribute` in order to create a distributable version of caffe with all necessary headers, binaries, etc. in `distribute/`. 137 | 138 | On my machine, with Caffe fully built, I’ve got the following basic layout in my CAFFE_ROOT dir: 139 | 140 | ``` 141 | caffe/ 142 | build/ 143 | python/ 144 | lib/ 145 | tools/ 146 | caffe ← this is our main binary 147 | distribute/ 148 | python/ 149 | lib/ 150 | include/ 151 | bin/ 152 | proto/ 153 | ``` 154 | 155 | At this point, we have everything we need to train, test, and program with neural 156 | networks. In the next section we’ll add a user-friendly, web-based front end to 157 | Caffe called DIGITS, which will make training and testing our networks much easier. 158 | 159 | ### Option 1b: Installing DIGITS Natively 160 | 161 | nVidia’s [Deep Learning GPU Training System, or DIGITS](https://github.com/NVIDIA/DIGITS), 162 | is BSD-licensed Python web app for training neural networks. While it’s 163 | possible to do everything DIGITS does in Caffe at the command-line, or with code, 164 | using DIGITS makes it a lot easier to get started. I also found it more fun, due 165 | to the great visualizations, real-time charts, and other graphical features. 166 | Since you’re experimenting and trying to learn, I highly recommend beginning with DIGITS. 167 | 168 | There are quite a few good docs at https://github.com/NVIDIA/DIGITS/tree/master/docs, 169 | including a few [Installation](https://github.com/NVIDIA/DIGITS/blob/master/docs/BuildDigits.md), 170 | [Configuration](https://github.com/NVIDIA/DIGITS/blob/master/docs/Configuration.md), 171 | and [Getting Started](https://github.com/NVIDIA/DIGITS/blob/master/docs/GettingStarted.md) 172 | pages. I’d recommend reading through everything there before you continue, as I’m not 173 | an expert on everything you can do with DIGITS. There's also a public [DIGITS User Group](https://groups.google.com/forum/#!forum/digits-users) if you have questions you need to ask. 174 | 175 | There are various ways to install and run DIGITS, from Docker to pre-baked packages 176 | on Linux, or you can build it from source. I’m on a Mac, so I built it from source. 177 | 178 | **NOTE:** In my walkthrough I've used the following non-released version of DIGITS 179 | from their Github repo: https://github.com/NVIDIA/DIGITS/commit/81be5131821ade454eb47352477015d7c09753d9 180 | 181 | Because it’s just a bunch of Python scripts, it was fairly painless to get working. 182 | The one thing you need to do is tell DIGITS where your `CAFFE_ROOT` is by setting 183 | an environment variable before starting the server: 184 | 185 | ```bash 186 | export CAFFE_ROOT=/path/to/caffe 187 | ./digits-devserver 188 | ``` 189 | 190 | NOTE: on Mac I had issues with the server scripts assuming my Python binary was 191 | called `python2`, where I only have `python2.7`. You can symlink it in `/usr/bin` 192 | or modify the DIGITS startup script(s) to use the proper binary on your system. 193 | 194 | Once the server is started, you can do everything else via your web browser at http://localhost:5000, which is what I'll do below. 195 | 196 | ### Option 2: Caffe and DIGITS using Docker 197 | 198 | Install [Docker](https://www.docker.com/), if not already installed, then run the following command 199 | in order to pull and run a full Caffe + Digits container. A few things to note: 200 | * make sure port 8080 isn't allocated by another program. If so, change it to any other port you want. 201 | * change `/path/to/this/repository` to the location of this cloned repo, and `/data/repo` within the container 202 | will be bound to this directory. This is useful for accessing the images discussed below. 203 | 204 | ```bash 205 | docker run --name digits -d -p 8080:5000 -v /path/to/this/repository:/data/repo kaixhin/digits 206 | ``` 207 | 208 | Now that we have our container running you can open up your web browser and open `http://localhost:8080`. Everything in the repository is now in the container directory `/data/repo`. That's it. You've now got Caffe and DIGITS working. 209 | 210 | If you need shell access, use the following command: 211 | 212 | ```bash 213 | docker exec -it digits /bin/bash 214 | ``` 215 | 216 | ## Training a Neural Network 217 | 218 | Training a neural network involves a few steps: 219 | 220 | 1. Assemble and prepare a dataset of categorized images 221 | 2. Define the network’s architecture 222 | 3. Train and Validate this network using the prepared dataset 223 | 224 | We’re going to do this 3 different ways, in order to show the difference 225 | between starting from scratch and using a pretrained network, and also to show 226 | how to work with two popular pretrained networks (AlexNet, GoogLeNet) that are 227 | commonly used with Caffe and DIGITs. 228 | 229 | For our training attempts, we’ll use a small dataset of Dolphins and Seahorses. 230 | I’ve put the images I used in [data/dolphins-and-seahorses](data/dolphins-and-seahorses). 231 | You need at least 2 categories, but could have many more (some of the networks 232 | we’ll use were trained on 1000+ image categories). Our goal is to be able to 233 | give an image to our network and have it tell us whether it’s a Dolphin or a Seahorse. 234 | 235 | ### Prepare the Dataset 236 | 237 | The easiest way to begin is to divide your images into a categorized directory layout: 238 | 239 | ``` 240 | dolphins-and-seahorses/ 241 | dolphin/ 242 | image_0001.jpg 243 | image_0002.jpg 244 | image_0003.jpg 245 | ... 246 | seahorse/ 247 | image_0001.jpg 248 | image_0002.jpg 249 | image_0003.jpg 250 | ... 251 | ``` 252 | 253 | Here each directory is a category we want to classify, and each image within 254 | that category dir an example we’ll use for training and validation. 255 | 256 | > Q: “Do my images have to be the same size? What about the filenames, do they matter?” 257 | 258 | No to both. The images sizes will be normalized before we feed them into 259 | the network. We’ll eventually want colour images of 256 x 256 pixels, but 260 | DIGITS will crop or squash (we'll squash) our images automatically in a moment. 261 | The filenames are irrelevant--it’s only important which category they are contained 262 | within. 263 | 264 | > Q: “Can I do more complex segmentation of my categories?” 265 | 266 | Yes. See https://github.com/NVIDIA/DIGITS/blob/digits-4.0/docs/ImageFolderFormat.md. 267 | 268 | We want to use these images on disk to create a **New Dataset**, and specifically, 269 | a **Classification Dataset**. 270 | 271 | ![Create New Dataset](images/create-new-dataset.png?raw=true "Create New Dataset") 272 | 273 | We’ll use the defaults DIGITS gives us, and point **Training Images** at the path 274 | to our [data/dolphins-and-seahorses](data/dolphins-and-seahorses) folder. 275 | DIGITS will use the categories (`dolphin` and `seahorse`) to create a database 276 | of squashed, 256 x 256 Training (75%) and Testing (25%) images. 277 | 278 | Give your Dataset a name,`dolphins-and-seahorses`, and click **Create**. 279 | 280 | ![New Image Classification Dataset](images/new-image-classification-dataset.png?raw=true "New Image Classification Dataset") 281 | 282 | This will create our dataset, which took only 4s on my laptop. In the end I 283 | have 92 Training images (49 dolphin, 43 seahorse) in 2 categories, with 30 284 | Validation images (16 dolphin, 14 seahorse). It’s a really small dataset, but perfect 285 | for our experimentation and learning purposes, because it won’t take forever to train 286 | and validate a network that uses it. 287 | 288 | You can **Explore the db** if you want to see the images after they have been squashed. 289 | 290 | ![Explore the db](images/explore-dataset.png?raw=true "Explore the db") 291 | 292 | ### Training: Attempt 1, from Scratch 293 | 294 | Back in the DIGITS Home screen, we need to create a new **Classification Model**: 295 | 296 | ![Create Classification Model](images/create-classification-model.png?raw=true "Create Classification Model") 297 | 298 | We’ll start by training a model that uses our `dolphins-and-seahorses` dataset, 299 | and the default settings DIGITS provides. For our first network, we’ll choose to 300 | use one of the standard network architectures, [AlexNet (pdf)](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf). [AlexNet’s design](http://vision.stanford.edu/teaching/cs231b_spring1415/slides/alexnet_tugce_kyunghee.pdf) 301 | won a major computer vision competition called ImageNet in 2012. The competition 302 | required categorizing 1000+ image categories across 1.2 million images. 303 | 304 | ![New Classification Model 1](images/new-image-classification-model-attempt1.png?raw=true "Model 1") 305 | 306 | Caffe uses structured text files to define network architectures. These text files 307 | are based on [Google’s Protocol Buffers](https://developers.google.com/protocol-buffers/). 308 | You can read the [full schema](https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto) Caffe uses. 309 | For the most part we’re not going to work with these, but it’s good to be aware of their 310 | existence, since we’ll have to modify them in later steps. The AlexNet prototxt file 311 | looks like this, for example: https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt. 312 | 313 | We’ll train our network for **30 epochs**, which means that it will learn (with our 314 | training images) then test itself (using our validation images), and adjust the 315 | network’s weights depending on how well it’s doing, and repeat this process 30 times. 316 | Each time it completes a cycle we’ll get info about **Accuracy** (0% to 100%, 317 | where higher is better) and what our **Loss** is (the sum of all the mistakes that were 318 | made, where lower is better). Ideally we want a network that is able to predict with 319 | high accuracy, and with few errors (small loss). 320 | 321 | **NOTE:** some people have [reported hitting errors in DIGITS](https://github.com/humphd/have-fun-with-machine-learning/issues/17) 322 | doing this training run. For many, the problem related to available memory (the process 323 | needs a lot of memory to work). If you're using Docker, you might want to try 324 | increasing the amount of memory available to DIGITS (in Docker, preferences -> advanced -> memory). 325 | 326 | Initially, our network’s accuracy is a bit below 50%. This makes sense, because at first it’s 327 | just “guessing” between two categories using randomly assigned weights. Over time 328 | it’s able to achieve 87.5% accuracy, with a loss of 0.37. The entire 30 epoch run 329 | took me just under 6 minutes. 330 | 331 | ![Model Attempt 1](images/model-attempt1.png?raw=true "Model Attempt 1") 332 | 333 | We can test our model using an image we upload or a URL to an image on the web. 334 | Let’s test it on a few examples that weren’t in our training/validation dataset: 335 | 336 | ![Model 1 Classify 1](images/model-attempt1-classify1.png?raw=true "Model 1 Classify 1") 337 | 338 | ![Model 1 Classify 2](images/model-attempt1-classify2.png?raw=true "Model 1 Classify 2") 339 | 340 | It almost seems perfect, until we try another: 341 | 342 | ![Model 1 Classify 3](images/model-attempt1-classify3.png?raw=true "Model 1 Classify 3") 343 | 344 | Here it falls down completely, and confuses a seahorse for a dolphin, and worse, 345 | does so with a high degree of confidence. 346 | 347 | The reality is that our dataset is too small to be useful for training a really good 348 | neural network. We really need 10s or 100s of thousands of images, and with that, a 349 | lot of computing power to process everything. 350 | 351 | ### Training: Attempt 2, Fine Tuning AlexNet 352 | 353 | #### How Fine Tuning works 354 | 355 | Designing a neural network from scratch, collecting data sufficient to train 356 | it (e.g., millions of images), and accessing GPUs for weeks to complete the 357 | training is beyond the reach of most of us. To make it practical for smaller amounts 358 | of data to be used, we employ a technique called **Transfer Learning**, or **Fine Tuning**. 359 | Fine tuning takes advantage of the layout of deep neural networks, and uses 360 | pretrained networks to do the hard work of initial object detection. 361 | 362 | Imagine using a neural network to be like looking at something far away with a 363 | pair of binoculars. You first put the binoculars to your eyes, and everything is 364 | blurry. As you adjust the focus, you start to see colours, lines, shapes, and eventually 365 | you are able to pick out the shape of a bird, then with some more adjustment you can 366 | identify the species of bird. 367 | 368 | In a multi-layered network, the initial layers extract features (e.g., edges), with 369 | later layers using these features to detect shapes (e.g., a wheel, an eye), which are 370 | then feed into final classification layers that detect items based on accumulated 371 | characteristics from previous layers (e.g., a cat vs. a dog). A network has to be 372 | able to go from pixels to circles to eyes to two eyes placed in a particular orientation, 373 | and so on up to being able to finally conclude that an image depicts a cat. 374 | 375 | What we’d like to do is to specialize an existing, pretrained network for classifying 376 | a new set of image classes instead of the ones on which it was initially trained. Because 377 | the network already knows how to “see” features in images, we’d like to retrain 378 | it to “see” our particular image types. We don’t need to start from scratch with the 379 | majority of the layers--we want to transfer the learning already done in these layers 380 | to our new classification task. Unlike our previous attempt, which used random weights, 381 | we’ll use the existing weights of the final network in our training. However, we’ll 382 | throw away the final classification layer(s) and retrain the network with *our* image 383 | dataset, fine tuning it to our image classes. 384 | 385 | For this to work, we need a pretrained network that is similar enough to our own data 386 | that the learned weights will be useful. Luckily, the networks we’ll use below were 387 | trained on millions of natural images from [ImageNet](http://image-net.org/), which 388 | is useful across a broad range of classification tasks. 389 | 390 | This technique has been used to do interesting things like screening for eye diseases 391 | from medical imagery, identifying plankton species from microscopic images collected at 392 | sea, to categorizing the artistic style of Flickr images. 393 | 394 | Doing this perfectly, like all of machine learning, requires you to understand the 395 | data and network architecture--you have to be careful with overfitting of the data, 396 | might need to fix some of the layers, might need to insert new layers, etc. However, 397 | my experience is that it “Just Works” much of the time, and it’s worth you simply doing 398 | an experiment to see what you can achieve using our naive approach. 399 | 400 | #### Uploading Pretrained Networks 401 | 402 | In our first attempt, we used AlexNet’s architecture, but started with random 403 | weights in the network’s layers. What we’d like to do is download and use a 404 | version of AlexNet that has already been trained on a massive dataset. 405 | 406 | Thankfully we can do exactly this. A snapshot of AlexNet is available for download: https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet. 407 | We need the binary `.caffemodel` file, which is what contains the trained weights, and it’s 408 | available for download at http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel. 409 | 410 | While you’re downloading pretrained models, let’s get one more at the same time. 411 | In 2014, Google won the same ImageNet competition with [GoogLeNet](https://research.google.com/pubs/pub43022.html) (codenamed Inception): 412 | a 22-layer neural network. A snapshot of GoogLeNet is available for download 413 | as well, see https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenet. 414 | Again, we’ll need the `.caffemodel` file with all the pretrained weights, 415 | which is available for download at http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel. 416 | 417 | With these `.caffemodel` files in hand, we can upload them into DIGITs. Go to 418 | the **Pretrained Models** tab in DIGITs home page and choose **Upload Pretrained Model**: 419 | 420 | ![Load Pretrained Model](images/load-pretrained-model.png?raw=true "Load Pretrained Model") 421 | 422 | For both of these pretrained models, we can use the defaults DIGITs provides 423 | (i.e., colour, squashed images of 256 x 256). We just need to provide the 424 | `Weights (**.caffemodel)` and `Model Definition (original.prototxt)`. 425 | Click each of those buttons to select a file. 426 | 427 | For the model definitions we can use https://github.com/BVLC/caffe/blob/master/models/bvlc_googlenet/train_val.prototxt 428 | for GoogLeNet and https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt 429 | for AlexNet. We aren’t going to use the classification labels of these networks, 430 | so we’ll skip adding a `labels.txt` file: 431 | 432 | ![Upload Pretrained Model](images/upload-pretrained-model.png?raw=true "Upload Pretrained Model") 433 | 434 | Repeat this process for both AlexNet and GoogLeNet, as we’ll use them both in the coming steps. 435 | 436 | > Q: "Are there other networks that would be good as a basis for fine tuning?" 437 | 438 | The [Caffe Model Zoo](http://caffe.berkeleyvision.org/model_zoo.html) has quite a few other 439 | pretrained networks that could be used, see https://github.com/BVLC/caffe/wiki/Model-Zoo. 440 | 441 | #### Fine Tuning AlexNet for Dolphins and Seahorses 442 | 443 | Training a network using a pretrained Caffe Model is similar to starting from scratch, 444 | though we have to make a few adjustments. First, we’ll adjust the **Base Learning Rate** 445 | to 0.001 from 0.01, since we don’t need to make such large jumps (i.e., we’re fine tuning). 446 | We’ll also use a **Pretrained Network**, and **Customize** it. 447 | 448 | ![New Image Classification](images/new-image-classification-model-attempt2.png?raw=true "New Image Classification") 449 | 450 | In the pretrained model’s definition (i.e., prototext), we need to rename all 451 | references to the final **Fully Connected Layer** (where the end result classifications 452 | happen). We do this because we want the model to re-learn new categories from 453 | our dataset vs. its original training data (i.e., we want to throw away the current 454 | final layer). We have to rename the last fully connected layer from “fc8” to 455 | something else, “fc9” for example. Finally, we also need to adjust the number 456 | of categories from `1000` to `2`, by changing `num_output` to `2`. 457 | 458 | Here are the changes we need to make: 459 | 460 | ```diff 461 | @@ -332,8 +332,8 @@ 462 | } 463 | layer { 464 | - name: "fc8" 465 | + name: "fc9" 466 | type: "InnerProduct" 467 | bottom: "fc7" 468 | - top: "fc8" 469 | + top: "fc9" 470 | param { 471 | lr_mult: 1 472 | @@ -345,5 +345,5 @@ 473 | } 474 | inner_product_param { 475 | - num_output: 1000 476 | + num_output: 2 477 | weight_filler { 478 | type: "gaussian" 479 | @@ -359,5 +359,5 @@ 480 | name: "accuracy" 481 | type: "Accuracy" 482 | - bottom: "fc8" 483 | + bottom: "fc9" 484 | bottom: "label" 485 | top: "accuracy" 486 | @@ -367,5 +367,5 @@ 487 | name: "loss" 488 | type: "SoftmaxWithLoss" 489 | - bottom: "fc8" 490 | + bottom: "fc9" 491 | bottom: "label" 492 | top: "loss" 493 | @@ -375,5 +375,5 @@ 494 | name: "softmax" 495 | type: "Softmax" 496 | - bottom: "fc8" 497 | + bottom: "fc9" 498 | top: "softmax" 499 | include { stage: "deploy" } 500 | ``` 501 | 502 | I’ve included the fully modified file I’m using in [src/alexnet-customized.prototxt](src/alexnet-customized.prototxt). 503 | 504 | This time our accuracy starts at ~60% and climbs right away to 87.5%, then to 96% 505 | and all the way up to 100%, with the Loss steadily decreasing. After 5 minutes we 506 | end up with an accuracy of 100% and a loss of 0.0009. 507 | 508 | ![Model Attempt 2](images/model-attempt2.png?raw=true "Model Attempt 2") 509 | 510 | Testing the same seahorse image our previous network got wrong, we see a complete 511 | reversal: 100% seahorse. 512 | 513 | ![Model 2 Classify 1](images/model-attempt2-classify1.png?raw=true "Model 2 Classify 1") 514 | 515 | Even a children’s drawing of a seahorse works: 516 | 517 | ![Model 2 Classify 2](images/model-attempt2-classify2.png?raw=true "Model 2 Classify 2") 518 | 519 | The same goes for a dolphin: 520 | 521 | ![Model 2 Classify 3](images/model-attempt2-classify3.png?raw=true "Model 2 Classify 3") 522 | 523 | Even with images that you think might be hard, like this one that has multiple dolphins 524 | close together, and with their bodies mostly underwater, it does the right thing: 525 | 526 | ![Model 2 Classify 4](images/model-attempt2-classify4.png?raw=true "Model 2 Classify 4") 527 | 528 | ### Training: Attempt 3, Fine Tuning GoogLeNet 529 | 530 | Like the previous AlexNet model we used for fine tuning, we can use GoogLeNet as well. 531 | Modifying the network is a bit trickier, since you have to redefine three fully 532 | connected layers instead of just one. 533 | 534 | To fine tune GoogLeNet for our use case, we need to once again create a 535 | new **Classification Model**: 536 | 537 | ![New Classification Model](images/new-image-classification-model-attempt3.png?raw=true "New Classification Model") 538 | 539 | We rename all references to the three fully connected classification layers, 540 | `loss1/classifier`, `loss2/classifier`, and `loss3/classifier`, and redefine 541 | the number of categories (`num_output: 2`). Here are the changes we need to make 542 | in order to rename the 3 classifier layers, as well as to change from 1000 to 2 categories: 543 | 544 | ```diff 545 | @@ -917,10 +917,10 @@ 546 | exclude { stage: "deploy" } 547 | } 548 | layer { 549 | - name: "loss1/classifier" 550 | + name: "loss1a/classifier" 551 | type: "InnerProduct" 552 | bottom: "loss1/fc" 553 | - top: "loss1/classifier" 554 | + top: "loss1a/classifier" 555 | param { 556 | lr_mult: 1 557 | decay_mult: 1 558 | @@ -930,7 +930,7 @@ 559 | decay_mult: 0 560 | } 561 | inner_product_param { 562 | - num_output: 1000 563 | + num_output: 2 564 | weight_filler { 565 | type: "xavier" 566 | std: 0.0009765625 567 | @@ -945,7 +945,7 @@ 568 | layer { 569 | name: "loss1/loss" 570 | type: "SoftmaxWithLoss" 571 | - bottom: "loss1/classifier" 572 | + bottom: "loss1a/classifier" 573 | bottom: "label" 574 | top: "loss1/loss" 575 | loss_weight: 0.3 576 | @@ -954,7 +954,7 @@ 577 | layer { 578 | name: "loss1/top-1" 579 | type: "Accuracy" 580 | - bottom: "loss1/classifier" 581 | + bottom: "loss1a/classifier" 582 | bottom: "label" 583 | top: "loss1/accuracy" 584 | include { stage: "val" } 585 | @@ -962,7 +962,7 @@ 586 | layer { 587 | name: "loss1/top-5" 588 | type: "Accuracy" 589 | - bottom: "loss1/classifier" 590 | + bottom: "loss1a/classifier" 591 | bottom: "label" 592 | top: "loss1/accuracy-top5" 593 | include { stage: "val" } 594 | @@ -1705,10 +1705,10 @@ 595 | exclude { stage: "deploy" } 596 | } 597 | layer { 598 | - name: "loss2/classifier" 599 | + name: "loss2a/classifier" 600 | type: "InnerProduct" 601 | bottom: "loss2/fc" 602 | - top: "loss2/classifier" 603 | + top: "loss2a/classifier" 604 | param { 605 | lr_mult: 1 606 | decay_mult: 1 607 | @@ -1718,7 +1718,7 @@ 608 | decay_mult: 0 609 | } 610 | inner_product_param { 611 | - num_output: 1000 612 | + num_output: 2 613 | weight_filler { 614 | type: "xavier" 615 | std: 0.0009765625 616 | @@ -1733,7 +1733,7 @@ 617 | layer { 618 | name: "loss2/loss" 619 | type: "SoftmaxWithLoss" 620 | - bottom: "loss2/classifier" 621 | + bottom: "loss2a/classifier" 622 | bottom: "label" 623 | top: "loss2/loss" 624 | loss_weight: 0.3 625 | @@ -1742,7 +1742,7 @@ 626 | layer { 627 | name: "loss2/top-1" 628 | type: "Accuracy" 629 | - bottom: "loss2/classifier" 630 | + bottom: "loss2a/classifier" 631 | bottom: "label" 632 | top: "loss2/accuracy" 633 | include { stage: "val" } 634 | @@ -1750,7 +1750,7 @@ 635 | layer { 636 | name: "loss2/top-5" 637 | type: "Accuracy" 638 | - bottom: "loss2/classifier" 639 | + bottom: "loss2a/classifier" 640 | bottom: "label" 641 | top: "loss2/accuracy-top5" 642 | include { stage: "val" } 643 | @@ -2435,10 +2435,10 @@ 644 | } 645 | } 646 | layer { 647 | - name: "loss3/classifier" 648 | + name: "loss3a/classifier" 649 | type: "InnerProduct" 650 | bottom: "pool5/7x7_s1" 651 | - top: "loss3/classifier" 652 | + top: "loss3a/classifier" 653 | param { 654 | lr_mult: 1 655 | decay_mult: 1 656 | @@ -2448,7 +2448,7 @@ 657 | decay_mult: 0 658 | } 659 | inner_product_param { 660 | - num_output: 1000 661 | + num_output: 2 662 | weight_filler { 663 | type: "xavier" 664 | } 665 | @@ -2461,7 +2461,7 @@ 666 | layer { 667 | name: "loss3/loss" 668 | type: "SoftmaxWithLoss" 669 | - bottom: "loss3/classifier" 670 | + bottom: "loss3a/classifier" 671 | bottom: "label" 672 | top: "loss" 673 | loss_weight: 1 674 | @@ -2470,7 +2470,7 @@ 675 | layer { 676 | name: "loss3/top-1" 677 | type: "Accuracy" 678 | - bottom: "loss3/classifier" 679 | + bottom: "loss3a/classifier" 680 | bottom: "label" 681 | top: "accuracy" 682 | include { stage: "val" } 683 | @@ -2478,7 +2478,7 @@ 684 | layer { 685 | name: "loss3/top-5" 686 | type: "Accuracy" 687 | - bottom: "loss3/classifier" 688 | + bottom: "loss3a/classifier" 689 | bottom: "label" 690 | top: "accuracy-top5" 691 | include { stage: "val" } 692 | @@ -2489,7 +2489,7 @@ 693 | layer { 694 | name: "softmax" 695 | type: "Softmax" 696 | - bottom: "loss3/classifier" 697 | + bottom: "loss3a/classifier" 698 | top: "softmax" 699 | include { stage: "deploy" } 700 | } 701 | ``` 702 | 703 | I’ve put the complete file in [src/googlenet-customized.prototxt](src/googlenet-customized.prototxt). 704 | 705 | > Q: "What about changes to the prototext definitions of these networks? 706 | > We changed the fully connected layer name(s), and the number of categories. 707 | > What else could, or should be changed, and in what circumstances?" 708 | 709 | Great question, and it's something I'm wondering, too. For example, I know that we can 710 | ["fix" certain layers](https://github.com/BVLC/caffe/wiki/Fine-Tuning-or-Training-Certain-Layers-Exclusively) 711 | so the weights don't change. Doing other things involves understanding how the layers work, 712 | which is beyond this guide, and also beyond its author at present! 713 | 714 | Like we did with fine tuning AlexNet, we also reduce the learning rate by 715 | 10% from `0.01` to `0.001`. 716 | 717 | > Q: "What other changes would make sense when fine tuning these networks? 718 | > What about different numbers of epochs, batch sizes, solver types (Adam, AdaDelta, AdaGrad, etc), 719 | > learning rates, policies (Exponential Decay, Inverse Decay, Sigmoid Decay, etc), 720 | > step sizes, and gamma values?" 721 | 722 | Great question, and one that I wonder about as well. I only have a vague understanding of these 723 | and it’s likely that there are improvements we can make if you know how to alter these 724 | values when training. This is something that needs better documentation. 725 | 726 | Because GoogLeNet has a more complicated architecture than AlexNet, fine tuning it requires 727 | more time. On my laptop, it takes 10 minutes to retrain GoogLeNet with our dataset, 728 | achieving 100% accuracy and a loss of 0.0070: 729 | 730 | ![Model Attempt 3](images/model-attempt3.png?raw=true "Model Attempt 3") 731 | 732 | Just as we saw with the fine tuned version of AlexNet, our modified GoogLeNet 733 | performs amazing well--the best so far: 734 | 735 | ![Model Attempt 3 Classify 1](images/model-attempt3-classify1.png?raw=true "Model Attempt 3 Classify 1") 736 | 737 | ![Model Attempt 3 Classify 2](images/model-attempt3-classify2.png?raw=true "Model Attempt 3 Classify 2") 738 | 739 | ![Model Attempt 3 Classify 3](images/model-attempt3-classify3.png?raw=true "Model Attempt 3 Classify 3") 740 | 741 | ## Using our Model 742 | 743 | With our network trained and tested, it’s time to download and use it. Each of the models 744 | we trained in DIGITS has a **Download Model** button, as well as a way to select different 745 | snapshots within our training run (e.g., `Epoch #30`): 746 | 747 | ![Trained Models](images/trained-models.png?raw=true "Trained Models") 748 | 749 | Clicking **Download Model** downloads a `tar.gz` archive containing the following files: 750 | 751 | ``` 752 | deploy.prototxt 753 | mean.binaryproto 754 | solver.prototxt 755 | info.json 756 | original.prototxt 757 | labels.txt 758 | snapshot_iter_90.caffemodel 759 | train_val.prototxt 760 | ``` 761 | 762 | There’s a [nice description](https://github.com/BVLC/caffe/wiki/Using-a-Trained-Network:-Deploy) in 763 | the Caffe documentation about how to use the model we just built. It says: 764 | 765 | > A network is defined by its design (.prototxt), and its weights (.caffemodel). As a network is 766 | > being trained, the current state of that network's weights are stored in a .caffemodel. With both 767 | > of these we can move from the train/test phase into the production phase. 768 | > 769 | > In its current state, the design of the network is not designed for deployment. Before we can 770 | > release our network as a product, we often need to alter it in a few ways: 771 | > 772 | > 1. Remove the data layer that was used for training, as for in the case of classification we are no longer providing labels for our data. 773 | > 2. Remove any layer that is dependent upon data labels. 774 | > 3. Set the network up to accept data. 775 | > 4. Have the network output the result. 776 | 777 | DIGITS has already done the work for us, separating out the different versions of our `prototxt` files. 778 | The files we’ll care about when using this network are: 779 | 780 | * `deploy.prototxt` - the definition of our network, ready for accepting image input data 781 | * `mean.binaryproto` - our model will need us to subtract the image mean from each image that it processes, and this is the mean image. 782 | * `labels.txt` - a list of our labels (`dolphin`, `seahorse`) in case we want to print them vs. just the category number 783 | * `snapshot_iter_90.caffemodel` - these are the trained weights for our network 784 | 785 | We can use these files in a number of ways to classify new images. For example, in our 786 | `CAFFE_ROOT` we can use `build/examples/cpp_classification/classification.bin` to classify one image: 787 | 788 | ```bash 789 | $ cd $CAFFE_ROOT/build/examples/cpp_classification 790 | $ ./classification.bin deploy.prototxt snapshot_iter_90.caffemodel mean.binaryproto labels.txt dolphin1.jpg 791 | ``` 792 | 793 | This will spit out a bunch of debug text, followed by the predictions for each of our two categories: 794 | 795 | ``` 796 | 0.9997 - “dolphin” 797 | 0.0003 - “seahorse” 798 | ``` 799 | 800 | You can read the [complete C++ source](https://github.com/BVLC/caffe/tree/master/examples/cpp_classification) 801 | for this in the [Caffe examples](https://github.com/BVLC/caffe/tree/master/examples). 802 | 803 | For a classification version that uses the Python interface, DIGITS includes a [nice example](https://github.com/NVIDIA/DIGITS/tree/master/examples/classification). There's also a fairly 804 | [well documented Python walkthrough](https://github.com/BVLC/caffe/blob/master/examples/00-classification.ipynb) in the Caffe examples. 805 | 806 | ### Python example 807 | 808 | Let's write a program that uses our fine-tuned GoogLeNet model to classify the untrained images 809 | we have in [data/untrained-samples](data/untrained-samples). I've cobbled this together based on 810 | the examples above, as well as the `caffe` [Python module's source](https://github.com/BVLC/caffe/tree/master/python), 811 | which you should prefer to anything I'm about to say. 812 | 813 | A full version of what I'm going to discuss is available in [src/classify-samples.py](src/classify-samples.py). 814 | Let's begin! 815 | 816 | First, we'll need the [NumPy](http://www.numpy.org/) module. In a moment we'll be using [NumPy](http://www.numpy.org/) 817 | to work with [`ndarray`s](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html), which Caffe uses a lot. 818 | If you haven't used them before, as I had not, you'd do well to begin by reading this 819 | [Quickstart tutorial](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html). 820 | 821 | Second, we'll need to load the `caffe` module from our `CAFFE_ROOT` dir. If it's not already included 822 | in your Python environment, you can force it to load by adding it manually. Along with it we'll 823 | also import caffe's protobuf module: 824 | 825 | ```python 826 | import numpy as np 827 | 828 | caffe_root = '/path/to/your/caffe_root' 829 | sys.path.insert(0, os.path.join(caffe_root, 'python')) 830 | import caffe 831 | from caffe.proto import caffe_pb2 832 | ``` 833 | 834 | Next we need to tell Caffe whether to [use the CPU or GPU](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L50-L52). 835 | For our experiments, the CPU is fine: 836 | 837 | ```python 838 | caffe.set_mode_cpu() 839 | ``` 840 | 841 | Now we can use `caffe` to load our trained network. To do so, we'll need some of the files we downloaded 842 | from DIGITS, namely: 843 | 844 | * `deploy.prototxt` - our "network file", the description of the network. 845 | * `snapshot_iter_90.caffemodel` - our trained "weights" 846 | 847 | We obviously need to provide the full path, and I'll assume that my files are in a dir called `model/`: 848 | 849 | ```python 850 | model_dir = 'model' 851 | deploy_file = os.path.join(model_dir, 'deploy.prototxt') 852 | weights_file = os.path.join(model_dir, 'snapshot_iter_90.caffemodel') 853 | net = caffe.Net(deploy_file, caffe.TEST, weights=weights_file) 854 | ``` 855 | 856 | The `caffe.Net()` [constructor](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L91-L117) 857 | takes a network file, a phase (`caffe.TEST` or `caffe.TRAIN`), as well as an optional weights filename. When 858 | we provide a weights file, the `Net` will automatically load them for us. The `Net` has a number of 859 | [methods and attributes](https://github.com/BVLC/caffe/blob/master/python/caffe/pycaffe.py) you can use. 860 | 861 | **Note:** There is also a [deprecated version of this constructor](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L119-L134), 862 | which seems to get used often in sample code on the web. It looks like this, in case you encounter it: 863 | 864 | ```python 865 | net = caffe.Net(str(deploy_file), str(model_file), caffe.TEST) 866 | ``` 867 | 868 | We're interested in loading images of various sizes into our network for testing. As a result, 869 | we'll need to *transform* them into a shape that our network can use (i.e., colour, 256x256). 870 | Caffe provides the [`Transformer` class](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L98) 871 | for this purpose. We'll use it to create a transformation appropriate for our images/network: 872 | 873 | ```python 874 | transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) 875 | # set_transpose: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L187 876 | transformer.set_transpose('data', (2, 0, 1)) 877 | # set_raw_scale: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L221 878 | transformer.set_raw_scale('data', 255) 879 | # set_channel_swap: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L203 880 | transformer.set_channel_swap('data', (2, 1, 0)) 881 | ``` 882 | 883 | We can also use the `mean.binaryproto` file DIGITS gave us to set our transformer's mean: 884 | 885 | ```python 886 | # This code for setting the mean from https://github.com/NVIDIA/DIGITS/tree/master/examples/classification 887 | mean_file = os.path.join(model_dir, 'mean.binaryproto') 888 | with open(mean_file, 'rb') as infile: 889 | blob = caffe_pb2.BlobProto() 890 | blob.MergeFromString(infile.read()) 891 | if blob.HasField('shape'): 892 | blob_dims = blob.shape 893 | assert len(blob_dims) == 4, 'Shape should have 4 dimensions - shape is %s' % blob.shape 894 | elif blob.HasField('num') and blob.HasField('channels') and \ 895 | blob.HasField('height') and blob.HasField('width'): 896 | blob_dims = (blob.num, blob.channels, blob.height, blob.width) 897 | else: 898 | raise ValueError('blob does not provide shape or 4d dimensions') 899 | pixel = np.reshape(blob.data, blob_dims[1:]).mean(1).mean(1) 900 | transformer.set_mean('data', pixel) 901 | ``` 902 | 903 | If we had a lot of labels, we might also choose to read in our labels file, which we can use 904 | later by looking up the label for a probability using its position (e.g., 0=dolphin, 1=seahorse): 905 | 906 | ```python 907 | labels_file = os.path.join(model_dir, 'labels.txt') 908 | labels = np.loadtxt(labels_file, str, delimiter='\n') 909 | ``` 910 | 911 | Now we're ready to classify an image. We'll use [`caffe.io.load_image()`](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L279) 912 | to read our image file, then use our transformer to reshape it and set it as our network's data layer: 913 | 914 | ```python 915 | # Load the image from disk using caffe's built-in I/O module 916 | image = caffe.io.load_image(fullpath) 917 | # Preprocess the image into the proper format for feeding into the model 918 | net.blobs['data'].data[...] = transformer.preprocess('data', image) 919 | ``` 920 | 921 | > Q: "How could I use images (i.e., frames) from a camera or video stream instead of files?" 922 | 923 | Great question, here's a skeleton to get you started: 924 | 925 | ```python 926 | import cv2 927 | ... 928 | # Get the shape of our input data layer, so we can resize the image 929 | input_shape = net.blobs['data'].data.shape 930 | ... 931 | webCamCap = cv2.VideoCapture(0) # could also be a URL, filename 932 | if webCamCap.isOpened(): 933 | rval, frame = webCamCap.read() 934 | else: 935 | rval = False 936 | 937 | while rval: 938 | rval, frame = webCamCap.read() 939 | net.blobs['data'].data[...] = transformer.preprocess('data', frame) 940 | ... 941 | 942 | webCamCap.release() 943 | ``` 944 | 945 | Back to our problem, we next need to run the image data through our network and read out 946 | the probabilities from our network's final `'softmax'` layer, which will be in order by label category: 947 | 948 | ```python 949 | # Run the image's pixel data through the network 950 | out = net.forward() 951 | # Extract the probabilities of our two categories from the final layer 952 | softmax_layer = out['softmax'] 953 | # Here we're converting to Python types from ndarray floats 954 | dolphin_prob = softmax_layer.item(0) 955 | seahorse_prob = softmax_layer.item(1) 956 | 957 | # Print the results. I'm using labels just to show how it's done 958 | label = labels[0] if dolphin_prob > seahorse_prob else labels[1] 959 | filename = os.path.basename(fullpath) 960 | print '%s is a %s dolphin=%.3f%% seahorse=%.3f%%' % (filename, label, dolphin_prob*100, seahorse_prob*100) 961 | ``` 962 | 963 | Running the full version of this (see [src/classify-samples.py](src/classify-samples.py)) using our 964 | fine-tuned GoogLeNet network on our [data/untrained-samples](data/untrained-samples) images gives 965 | me the following output: 966 | 967 | ``` 968 | [...truncated caffe network output...] 969 | dolphin1.jpg is a dolphin dolphin=99.968% seahorse=0.032% 970 | dolphin2.jpg is a dolphin dolphin=99.997% seahorse=0.003% 971 | dolphin3.jpg is a dolphin dolphin=99.943% seahorse=0.057% 972 | seahorse1.jpg is a seahorse dolphin=0.365% seahorse=99.635% 973 | seahorse2.jpg is a seahorse dolphin=0.000% seahorse=100.000% 974 | seahorse3.jpg is a seahorse dolphin=0.014% seahorse=99.986% 975 | ``` 976 | 977 | I'm still trying to learn all the best practices for working with models in code. I wish I had more 978 | and better documented code examples, APIs, premade modules, etc to show you here. To be honest, 979 | most of the code examples I’ve found are terse, and poorly documented--Caffe’s 980 | documentation is spotty, and assumes a lot. 981 | 982 | It seems to me like there’s an opportunity for someone to build higher-level tools on top of the 983 | Caffe interfaces for beginners and basic workflows like we've done here. It would be great if 984 | there were more simple modules in high-level languages that I could point you at that “did the 985 | right thing” with our model; someone could/should take this on, and make *using* Caffe 986 | models as easy as DIGITS makes *training* them. I’d love to have something I could use in node.js, 987 | for example. Ideally one shouldn’t be required to know so much about the internals of the model or Caffe. 988 | I haven’t used it yet, but [DeepDetect](https://deepdetect.com/) looks interesting on this front, 989 | and there are likely many other tools I don’t know about. 990 | 991 | ## Results 992 | 993 | At the beginning we said that our goal was to write a program that used a neural network to 994 | correctly classify all of the images in [data/untrained-samples](data/untrained-samples). 995 | These are images of dolphins and seahorses that were never used in the training or validation 996 | data: 997 | 998 | ### Untrained Dolphin Images 999 | 1000 | ![Dolphin 1](data/untrained-samples/dolphin1.jpg?raw=true "Dolphin 1") 1001 | ![Dolphin 2](data/untrained-samples/dolphin2.jpg?raw=true "Dolphin 2") 1002 | ![Dolphin 3](data/untrained-samples/dolphin3.jpg?raw=true "Dolphin 3") 1003 | 1004 | ### Untrained Seahorse Images 1005 | 1006 | ![Seahorse 1](data/untrained-samples/seahorse1.jpg?raw=true "Seahorse 1") 1007 | ![Seahorse 2](data/untrained-samples/seahorse2.jpg?raw=true "Seahorse 2") 1008 | ![Seahorse 3](data/untrained-samples/seahorse3.jpg?raw=true "Seahorse 3") 1009 | 1010 | Let's look at how each of our three attempts did with this challenge: 1011 | 1012 | ### Model Attempt 1: AlexNet from Scratch (3rd Place) 1013 | 1014 | | Image | Dolphin | Seahorse | Result | 1015 | |-------|---------|----------|--------| 1016 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 71.11% | 28.89% | :expressionless: | 1017 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.2% | 0.8% | :sunglasses: | 1018 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 63.3% | 36.7% | :confused: | 1019 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 95.04% | 4.96% | :disappointed: | 1020 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 56.64% | 43.36 | :confused: | 1021 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 7.06% | 92.94% | :grin: | 1022 | 1023 | ### Model Attempt 2: Fine Tuned AlexNet (2nd Place) 1024 | 1025 | | Image | Dolphin | Seahorse | Result | 1026 | |-------|---------|----------|--------| 1027 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.1% | 0.09% | :sunglasses: | 1028 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.5% | 0.05% | :sunglasses: | 1029 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 91.48% | 8.52% | :grin: | 1030 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0% | 100% | :sunglasses: | 1031 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 1032 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0% | 100% | :sunglasses: | 1033 | 1034 | ### Model Attempt 3: Fine Tuned GoogLeNet (1st Place) 1035 | 1036 | | Image | Dolphin | Seahorse | Result | 1037 | |-------|---------|----------|--------| 1038 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.86% | 0.14% | :sunglasses: | 1039 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 100% | 0% | :sunglasses: | 1040 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 100% | 0% | :sunglasses: | 1041 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0.5% | 99.5% | :sunglasses: | 1042 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 1043 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0.02% | 99.98% | :sunglasses: | 1044 | 1045 | ## Conclusion 1046 | 1047 | It’s amazing how well our model works, and what’s possible by fine tuning a pretrained network. 1048 | Obviously our dolphin vs. seahorse example is contrived, and the dataset overly limited--we really 1049 | do want more and better data if we want our network to be robust. But since our goal was to examine 1050 | the tools and workflows of neural networks, it’s turned out to be an ideal case, especially since it 1051 | didn’t require expensive equipment or massive amounts of time. 1052 | 1053 | Above all I hope that this experience helps to remove the overwhelming fear of getting started. 1054 | Deciding whether or not it’s worth investing time in learning the theories of machine learning and 1055 | neural networks is easier when you’ve been able to see it work in a small way. Now that you’ve got 1056 | a setup and a working approach, you can try doing other sorts of classifications. You might also look 1057 | at the other types of things you can do with Caffe and DIGITS, for example, finding objects within an 1058 | image, or doing segmentation. 1059 | 1060 | Have fun with machine learning! 1061 | -------------------------------------------------------------------------------- /README_ko-KR.md: -------------------------------------------------------------------------------- 1 | # Have Fun with Machine Learning: 초보자를 위한 가이드 2 | 3 | > Author: David Humphrey (original [English version](README.md)) 4 | > 한국어 번역:[zaysverse](https://github.com/zaysverse) 5 | 6 | 7 | 8 | 9 | ## 머리말 10 | 11 | 이것은 인공지능에 대한 *배경지식이 없는* 프로그래머들을 위한 머신러닝 **실습 가이드**입니다. 12 | 인공지능 신경망을 사용하는 것은 박사학위가 필요하지 않으며, 여러분은 크게 발전할 필요 없이 13 | 이미 있는 것을 *사용*하면 됩니다. 지금 우리가 가지고 있는 것은 충분히 유용합니다. 저는 이것을 14 | 다른 오픈소스를 대하는 것처럼 더 많은 사람이 갖고 놀아야 한다고 생각합니다. 15 | 16 | 이 가이드에서 우리의 목표는 머신러닝을 이용하여 [데이터/훈련되지 않은 샘플들](data/untrained-samples) 17 | 속 이미지가 **돌고래**인지 **해마**인지 이미지만으로 정확성 있게 예측하는 프로그램을 작성하는 것입니다. 18 | 여기 우리가 사용할 두 가지 예제 사진들이 있습니다: 19 | 20 | ![A dolphin](data/untrained-samples/dolphin1.jpg?raw=true "Dolphin") 21 | ![A seahorse](data/untrained-samples/seahorse1.jpg?raw=true "Seahorse") 22 | 23 | 그러기 위해 우리는 [나선형 신경망(CNN)](https://en.wikipedia.org/wiki/Convolutional_neural_network) 24 | 을 훈련하고 사용할 것입니다. 우리는 이것을 실무자의 관점 또는 첫 번째 원리의 관점에서 접근할 것입니다. 25 | 현재 인공지능에 많은 관심이 쏟아지고 있지만, 쓰인 대부분은 공원의 친구가 아니라 물리학교수가 자전거로 26 | 트릭을 가르치는 것처럼 느껴집니다. 27 | 28 | 저는 이것을 블로그 게시물처럼 깃허브 VS.에 작성하기로 했습니다. 제가 밑에 쓴 것 중 오해를 불러일으키거나 29 | 부족하거나 혹은 완전히 잘못된 부분이 있을 수 있습니다. 저는 아직 배워가는 중이고, 견고한 초보자용 문서가 없는 것이 30 | 장애물이라고 생각합니다. 실수가 있거나 중요한 세부사항이 누락된 것을 발견하셨다면, Pull request를 보내주십시오. 31 | 32 | 소개가 끝났으니, 여러분에게 자전거 트릭을 몇 가지 보여드리겠습니다! 33 | 34 | ## 개요 35 | 36 | 지금부터 살펴볼 내용은 다음과 같습니다: 37 | 38 | * 특히 기존의 오픈 소스 머신러닝 기술을 설정하고 사용합니다.([Caffe](http://caffe.berkeleyvision.org/)와 39 | [DIGITS](https://developer.nvidia.com/digits)) 40 | * 이미지 데이터셋을 만듭니다. 41 | * 신경망을 처음부터 훈련시킵니다. 42 | * 신경망이 겪은 적 없는 이미지로 신경망을 테스트합니다. 43 | * 기존 신경망을 하게 튜닝 -*fine Tuning*- 해 신경망의 정확성을 향상시킵니다. (AlexNet 와 GoogLeNet) 44 | * 신경망을 배포하고 사용합니다. 45 | 46 | 이 가이드는 신경망이 어떻게 설계되는지, 많은 이론을 다루거나, 수학적 표현을 사용하는 법을 47 | 가르쳐 주진 않습니다. 여러분에게 보여드릴 내용의 대부분을 이해한다고는 말하지 않겠습니다. 48 | 대신 흥미로운 방식으로 기존의 것들을 사용해 어려운 문제를 해결해 나갈 것입니다. 49 | 50 | > Q: "신경망의 이론에 관해서는 이야기하지 않는다고 말씀하셨습니다만, 앞으로 진행하기 전에 51 | > 적어도 목차(overview)가 필요하다고 생각합니다. 어디서부터 시작해야 할까요?" 52 | 53 | 이에 대한 소개는 짧은 게시물부터 온라인 전체 강좌까지 말 그대로 수백 가지가 넘습니다. 여러분이 54 | 배우고 싶은 방법에 따라 좋은 출밤점을 위한 3가지 선택지가 있습니다: 55 | 56 | * 이 멋진 [블로그 게시물](https://jalammar.github.io/visual-interactive-guide-basics-neural-networks/) 57 | 은 직관적인 예제들을 이용하여 신경망의 개념을 소개합니다. 58 | * 비슷하게, [브랜던 로러](https://www.youtube.com/channel/UCsBKTrp45lTfHa_p49I2AEQ)가 소개하는 59 | [이 영상](https://www.youtube.com/watch?v=FmpDIaiMIeA) 은 우리가 사용하게 될 나선형 신경망에 대 60 | 한 좋은 소개입니다. 61 | * 이론을 좀 더 알고 싶다면, [마이클 닐슨](http://michaelnielsen.org/) 의 [온라인 책](http://neuralnetworksanddeeplearning.com/chap1.html) 을 추천합니다. 62 | 63 | ## 설정 64 | 65 | 사용할 소프트웨어(Caffe와 DIGITS)는 플랫폼 및 운영체제 버전에 따라 설치가 어려울 수 있습니다. 가장 쉬운 방법은 66 | 도커(Docker)를 사용하는 것입니다. 아래에서 도커(Docker)로 하는 방법과 기본으로 설치하는 방법을 살펴봅시다. 67 | 68 | ### Option 1a: 네이티브하게 Caffe 설치 69 | 70 | 먼저, 우리는 버클리 비전 및 학습 센터의 [Caffe 딥러닝 프레임워크](http://caffe.berkeleyvision.org/) 71 | 를 사용할 것입니다. (BSD 라이센스) 72 | 73 | > Q: “잠깐만요, 왜 Caffe죠? Tensorflow와 같은 것을 사용하는 것은 어떨까요? 74 | > 요즘 모두가 말하는 것이잖아요...” 75 | 76 | 좋은 선택지가 많이 있고, 여러분은 모든 선택지를 살펴봐야 합니다. [TensorFlow](https://www.tensorflow.org/)는 77 | 훌륭하고 여러분은 TensorFlow를 사용해도 좋습니다. 하지만 전 여러 가지 이유로 Caffe를 사용하고 있습니다: 78 | 79 | * 컴퓨터 비전 문제에 적격입니다. 80 | * C++과 phyhon을 지원합니다.([node.js 지원](https://github.com/silklabs/node-caffe) 예정) 81 | * 빠르고 안정적입니다. 82 | 83 | 하지만 제가 Caffe를 사용하는 **첫 번째 이유**는 **어떤 코드도 쓸 필요 없기** 때문입니다. 여러분은 선언과 커맨드라인 84 | 도구로 모든 것을 할 수 있습니다. (Caffe는 구조화된 텍스트 파일을 사용하여 네트워크 아키텍처를 정의합니다) 또한, 85 | 여러분은 여러분의 네트워크를 더 쉽게 훈련하고 검증하기 위해 Caffe의 좋은 프론트 엔드들을 사용할 수 있습니다. 86 | 우리는 [nVidia의 DIGITS](https://developer.nvidia.com/digits) 도구를 이러한 목적으로 사용할 것입니다. 87 | 88 | Caffe는 설치하기에 힘들 수 있습니다. 미리 만들어진 Docker와 AWS 구성을 포함하여 다양한 플랫폼에 대한 [설치 지침](http://caffe.berkeleyvision.org/installation.html)이 있습니다. 89 | 90 | **NOTE:** 저는 Github repo에서 출시되지 않은 다음 버전의 Caffe를 사용했습니다: 91 | https://github.com/BVLC/caffe/commit/5a201dd960840c319cefd9fa9e2a40d2c76ddd73 92 | 93 | Mac에서는 버전 문제로 인해 빌드 내의 여러 단계에서 진행이 중단되어 작업을 시작하는 것이 어려울 수 94 | 있습니다. 이틀 동안 시행착오를 겪었습니다. 여러 가이드를 따라 해봤지만, 각각은 약간씩 다른 문제들을 95 | 가지고 있었습니다. 그 중 [이 가이드](https://gist.github.com/doctorpangloss/f8463bddce2a91b949639522ea1dcbe4)가 96 | 가장 가까웠습니다. 97 | 또한, [이 게시물](https://eddiesmo.wordpress.com/2016/12/20/how-to-set-up-caffe-environment-and-pycaffe-on-os-x-10-12-sierra/)을 추천합니다. 최근에 제가 봤던 많은 토론과 연결되어 있습니다. 98 | 99 | Caffe 설치는 저희가 할 것 중 가장 어려운 일입니다. 꽤 멋진 일이죠. AI쪽은 더 어려울 거라고 생각하셨을 테니까요! 100 | 몇 가지 문제를 겪으시더라도 포기하지 마세요. 그것은 그럴 가치가 있습니다. 만약 제가 이 작업을 다시 수행한다면, Mac에서 직접 수행하지 않고 Ubuntu VM을 사용할 것입니다. 도움이 더 필요하시다면, [Caffe 사용자](https://groups.google.com/forum/#!forum/caffe-users)그룹도 존재합니다. 101 | 102 | > Q: “신경망을 훈련시키려면 강력한 장비가 필요할까요? 좋은 GPU에 접근할 수 103 | > 없다면 어떻게 해야 할까요?" 104 | 105 | 사실 심층 신경망은 훈련시키기 위한 많은 연산능력과 에너지를 필요로 합니다.. 대규모 데이터셋을 이용해 처음부터 훈련시키는 경우라면 말입니다. 106 | 우리는 그렇게 하지 않을 거예요. 비결은 다른 사람이 이미 수백 시간에 걸쳐 훈련시켜논 사전 훈련된 신경망을 사용하여, 각자의 데이터셋에 맞게 107 | 미세하게 조정하는 것 -*Fine Tuning*-입니다. 아래에서 이 작업을 어떻게 하는지 알아보겠지만, 제가 여러분에게 보여드릴 것은 최신 GPU가 탑재되지 않은 1년 108 | 된 맥북 프로를 사용하고 있습니다. 109 | 110 | 이와는 별도로, 전 통합 인텔 그래픽 카드와 엔비디아 GPU를 가지고 있기 때문에 [OpenCL Caffe branch] 111 | (https://github.com/BVLC/caffe/tree/opencl)를 사용하기로 했고, 제 노트북에서 잘 작동했습니다. 112 | 113 | Caffe 설치가 완료되면 다음 작업을 수행하거나 수행해야 합니다: 114 | 115 | * 빌드된 caffe가 포함된 디렉터리입니다. 표준 방식으로 이 작업을 수행했다면, caffe, python 바인딩 등을 실행하는 116 | 데 필요한 모든 것이 `build/` 디렉터리에 있을 것입니다. `build/`의 상위 디렉터리는 `CAFFE_ROOT`(나중에 필요)입니다. 117 | * `make test && make runtest` 는 실행하지 마십시오. 118 | * 모든 python deps를 설치한 후(`python/`에서 `pip install -r requirements.txt` 실행), 119 | `make pycaffe && make pytest`는 실행하지 마십시오. 120 | * 또한 `distribute/` 안에 있는 모든 필수적인 헤더, 바이너리 등을 포함하는 배포 가능한 버전의 caffe를 생성하려면 121 | `make distribute`를 실행해야 합니다. 122 | 123 | Caffe가 완전히 빌드된 컴퓨터에서, CARRE_ROOT 디렉터리는 다음과 같은 기본 레이아웃을 따릅니다: 124 | 125 | ``` 126 | caffe/ 127 | build/ 128 | python/ 129 | lib/ 130 | tools/ 131 | caffe ← 메인 바이너리입니다. 132 | distribute/ 133 | python/ 134 | lib/ 135 | include/ 136 | bin/ 137 | proto/ 138 | ``` 139 | 140 | 이 시점에서 우리는 신경망으로 훈련, 테스트 및 프로그래밍하는 데 필요한 모든 것을 갖추고 있습니다. 다음 섹션에서는 141 | 사용자 친화적인 웹 기반 프론트 엔드를 DIGITS라고 불리는 caffe에 추가하여 신경망을 훨씬 쉽게 교육하고 테스트할 142 | 수 있습니다. 143 | 144 | ### Option 1b: 네이티브하게 DIGITS 설치 145 | 146 | nVidia의 [딥러닝 GPU 훈련시스템(DIGITS)](https://github.com/NVIDIA/DIGITS)는 신경망 훈련을 위한 147 | BSD 라이선스의 python 웹 앱입니다. 커맨드 라인이나 코드로 DIGITS가 Caffe에서 하는 모든 작업을 실행할 수 148 | 있지만, DIGITS를 사용하면 훨씬 쉽게 시작할 수 있습니다. 또한 뛰어난 시각화, 실시간 차트 및 기타 그래픽 기능들로 149 | 인해 더 재미있을 것입니다. 배우기 위해선 경험을 쌓고 도전해봐야 하므로 DIGITS로 시작하는 것을 추천합니다. 150 | 151 | https://github.com/NVIDIA/DIGITS/tree/master/docs 에 152 | [Installation](https://github.com/NVIDIA/DIGITS/blob/master/docs/BuildDigits.md)(설치), 153 | [Configuration](https://github.com/NVIDIA/DIGITS/blob/master/docs/Configuration.md)(구성), 154 | 및 [Getting Started](https://github.com/NVIDIA/DIGITS/blob/master/docs/GettingStarted.md) 155 | (시작) 페이지들을 포함하는 좋은 문서들이 꽤 있습니다. 전 DIGITS의 모든 것들을 잘 아는 전문가가 아니기 때문에 계속하기 156 | 전에 자세하게 읽어보는 걸 추천합니다. 도움이 더 필요하시다면, 공개 [DIGITS 사용자 그룹](https://groups.google.com/forum/#!forum/digits-users) 157 | 도 있습니다. 158 | 159 | Docker부터 리눅스에서 패키지들을 pre-baked하거나 소스에서 빌드하기까지, DIGITS를 설치하고 실행하는 데에는 다양한 160 | 방법이 있습니다. 저는 Mac을 사용하고 있음으로 소스에서 빌드했습니다. 161 | 162 | **NOTE:** 이 가이드에선 Github repo에서 출시되지 않은 DIGITS의 다음 버전을 사용했습니다 : https://github.com/NVIDIA/DIGITS/commit/81be5131821ade454eb47352477015d7c09753d9 163 | 164 | python 스크립트 묶음이기 때문에 작업하는 것은 힘들지 않았습니다. 여러분이 해야 할 것은 서버를 165 | 시작하기 전에 환경 변수를 설정하여 `CAFFE_ROOT`의 위치를 DIGITS에 알려주는 것입니다: 166 | 167 | ```bash 168 | export CAFFE_ROOT=/path/to/caffe 169 | ./digits-devserver 170 | ``` 171 | 172 | NOTE: Mac에서 파이썬 바이너리가 `pyhon2`라고 가정하고 서버 스크립트에 문제가 있었는데, 여기서 저는 173 | `python2.7`만 가지고 있었습니다. 이것은 `/usr/bin`에서 심볼릭링크로 접근하거나 DIGITS 부팅 시 174 | 스크립트를 조정하여 해결할 수 있습니다. 서버가 시작되면 http://localhost:5000 에 웹 브라우저를 175 | 통해 밑에서 다룰 모든 작업을 수행할 수 있습니다. 176 | 177 | ### Option 2: Docker를 사용한 Caffe와 DIGITS 178 | 179 | [Docker](https://www.docker.com/)를 설치하고(설치되어 있지 않은 경우) 전체 Caffe + Digits 180 | 컨테이너를 꺼내기 위해 다음 명령을 실행합니다. 몇 가지의 주의할 사항: 181 | * 포트 8080이 다른 프로그램에 할당되진 않았는지 확인하십시오. 만약 그렇다면, 임의의 다른 포트로 182 | 변경하십시오. 183 | * 이 복제(clone)된 repo의 위치를 `/path/to/this/repostiory`로 옮기십시오. 그러면 컨테이너 184 | 내의 `/data/repo`가 이 디렉터리에 바인딩됩니다. 이것은 아래 설명된 이미지에 접근하는 데 185 | 유용합니다. 186 | 187 | ```bash 188 | docker run --name digits -d -p 8080:5000 -v /path/to/this/repository:/data/repo kaixhin/digits 189 | ``` 190 | 191 | 이제 컨테이너가 실행 중이므로 우리는 웹 브라우저를 열고 `http://localhost:8080`에 접근할 수 있습니다. 192 | 이 repository의 모든 내용은 이제 컨테이너 디렉터리 `/data/repo`에 있습니다. 이제 다 했습니다. 이제 Caffe 193 | 와 DIGITS가 실행되고 있습니다. 194 | 셸에 접근이 필요한 경우, 다음 명령을 따라 하십시오: 195 | 196 | ```bash 197 | docker exec -it digits /bin/bash 198 | ``` 199 | 200 | ## 신경망 훈련 201 | 202 | 신경망을 훈련시키는 것은 몇 가지 단계를 수반합니다: 203 | 204 | 1. 분류된 이미지의 데이터셋을 구성하고 준비하십시오 205 | 2. 신경망의 아키텍처를 규정하십시오 206 | 3. 준비된 데이터셋을 사용해 이 신경망을 훈련시키고 검증하십시오. 207 | 208 | 처음부터 시작하는 것과 사전훈련된 신경망을 사용하는 것의 차이를 보여주고 Caffe와 DIGITs에서 흔히 209 | 사용되는 두 가지 인기 있는 사전훈련된 신경망(AlexNet, GoogLeNet)에서 어떻게 실행하는지 보여주기 210 | 위해 우리는 이러한 3단계를 거칠 것입니다. 211 | 212 | 우리는 훈련 시도에 돌고래와 해마의 작은 데이터셋을 사용할 것입니다. [data/dolphins-and-seahorses](data/dolphins-and-seahorses)에 제가 사용했던 이미지들을 넣어두었습니다. 2개 이상의 카테고리가 필요하고 여러분은 더 213 | 많은 카테고리를 가질 수도 있습니다(사용할 신경망 중 일부는 1,000개 이상의 이미지 카테고리에 대해 214 | 훈련되었습니다). 우리의 목표는 우리의 신경망에 이미지를 주고 그것이 돌고래인지 해마인지 우리에게 215 | 알려주게 하는 것입니다. 216 | 217 | ### 데이터셋 준비 218 | 219 | 가장 쉬운 방법은 이미지들을 분류된 디렉터리 배치로 나누는 것입니다.: 220 | 221 | ``` 222 | dolphins-and-seahorses/ 223 | dolphin/ 224 | image_0001.jpg 225 | image_0002.jpg 226 | image_0003.jpg 227 | ... 228 | seahorse/ 229 | image_0001.jpg 230 | image_0002.jpg 231 | image_0003.jpg 232 | ... 233 | ``` 234 | 235 | 여기 각 디렉터리는 이미지를 분류할 카테고리이며, 해당 카테고리 디렉터리 내의 각 이미지는 훈련 및 검증에 236 | 사용할 예제입니다. 237 | 238 | > Q: “이미지들의 사이즈가 같아야 하나요? 파일명은 어떻게 하죠, 그게 중요한가요?” 239 | 240 | 둘 다 아닙니다. 이미지 크기는 우리가 신경망에 입력하기 전에 정규화될 것입니다. 우리는 마지막엔 241 | 256 x 256 픽셀의 컬러 이미지를 사용하겠지만, DIGITS는 이미지를 자동으로 자르거나 스쿼시할 242 | 것입니다. 파일 이름은 관련이 없습니다--어떤 카테고리에 포함되느냐가 중요할 뿐입니다. 243 | 244 | > Q: “제 카테고리들을 더 복잡하게 세분화해도 되나요?” 245 | 246 | 네. https://github.com/NVIDIA/DIGITS/blob/digits-4.0/docs/ImageFolderFormat.md 를 참고하세요. 247 | 248 | 우리는 이 이미지를 디스크에 사용하여 **New Dataset**, 그중에서도 **Classification Dataset**을 249 | 생성하려고 합니다. 250 | 251 | ![Create New Dataset](images/create-new-dataset.png?raw=true "Create New Dataset") 252 | 253 | DIGITS가 제공하는 기본 설정값을 사용하고 [data/dolphins-and-seahorses](data/dolphins-and-seahorses) 254 | 폴더 경로에 **Training Images**를 지정합니다. DIGITS는 카테고리(`돌고래`와 `해마`)를 사용하여 255 | 스쿼시된 256 x 256 Training (75%) 및 Testing (25%) 이미지의 데이터베이스를 만듭니다. 256 | 257 | Dataset에 `dolphins-and-seahorses`라는 이름을 지정하고, **Create**를 클릭합니다. 258 | 259 | ![New Image Classification Dataset](images/new-image-classification-dataset.png?raw=true "New Image Classification Dataset") 260 | 261 | 이제 데이터셋이 생성될 것입니다. 제 노트북에선 4초 만에 생성되었죠. 마지막으로 2개의 카테고리 속 92개의 262 | 훈련 이미지 -*Training images*- (돌고래 49개, 해마 43개)와 30개의 검증 이미지 -*Validation images*- (돌고래 16개, 263 | 해마 14개)가 있습니다. 이것은 매우 작은 데이터셋이지만, 신경망을 훈련하고 검증하는 데 오랜 시간이 264 | 걸리지 않기 때문에 우리의 활동과 학습 목적에 알맞습니다. 265 | 266 | 이미지가 스쿼시된 후 이미지를 확인하려면 **DB 탐색** -*Explore DB*- 을 하면 됩니다. 267 | 268 | ![Explore the db](images/explore-dataset.png?raw=true "Explore the db") 269 | 270 | ### 훈련: 시도 1, 처음부터 271 | 272 | DIGITS 홈 화면으로 돌아가서, 우리는 새로운 **분류 모델** -*Classification Model*- 을 생성해야 합니다: 273 | 274 | ![Create Classification Model](images/create-classification-model.png?raw=true "Create Classification Model") 275 | 276 | 우리는 우리의 `dolphins-and-seahorses` 데이터셋과 DIGITS가 제공하는 기본 설정값을 277 | 사용하는 모델을 훈련시키는 것부터 시작할 것입니다 첫 번째 신경망으로는 표준 신경망 278 | 아키텍처 중 하나인 [AlexNet (pdf)](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf) 279 | 을 사용할 것입니다. [AlexNet 설계](http://vision.stanford.edu/teaching/cs231b_spring1415/slides/alexnet_tugce_kyunghee.pdf) 280 | 는 2012년 ImageNet이라는 큰 컴퓨터 비전 대회에서 우승했습니다. 이 대회는 120만 281 | 개의 이미지에 걸쳐 1,000개 이상의 이미지 카테고리를 분류해야 했습니다. 282 | 283 | ![New Classification Model 1](images/new-image-classification-model-attempt1.png?raw=true "Model 1") 284 | 285 | Caffe는 구조화된 텍스트 파일을 사용해 신경망 아키텍처를 정의합니다. 이러한 텍스트 파일은 286 | [Google의 프로토콜 버퍼](https://developers.google.com/protocol-buffers/)를 기반으로 합니다. 287 | 여러분은 Caffe가 사용하는 [전체적인 도식](https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto) 288 | 을 읽어보실 수 있습니다. 289 | 대부분의 파트에서 우리는 이것들을 사용하지 않겠지만, 나중에 그것들을 수정해줘야 하므로 290 | 이러한 것들이 있다는 것을 알아두는 것이 좋습니다. 291 | AlextNet protxt 파일은 다음과 같습니다: 292 | https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt. 293 | 294 | **epoch는 30**으로 신경망을 훈련시킵니다. 즉, 신경망이 학습(우리의 training image를 통해)하면 295 | 자체적으로 테스트(validation image를 사용해)하며 결과에 따라 신경망의 가중치를 조정하는 것을 296 | 30번 반복합니다. 한 사이클이 완료될 때마다 **Accuracy** (0%~100%, 높을수록 좋은 값)와 297 | **Loss**가 얼마인지(발생한 모든 오류의 합계, 낮을수록 좋은 값)에 대한 정보를 얻을 수 있습니다. 298 | 이상적으로는 우리는 오류(작은 손실 -*loss*-)가 거의 없는, 매우 정확하게 예측할 수 있는 신경망을 299 | 원합니다. 300 | 301 | **NOTE:** 몇몇 사람들이 이 훈련을 시키면서 [DIGITS에서 hit 오류가 일어났다](https://github.com/humphd/have-fun-with-machine-learning/issues/17) 302 | 고 보고했습니다. 대부분의 경우, 이 문제는 가용 메모리와 관련된 것입니다(프로세스가 실행하려면 많은 303 | 메모리가 필요합니다). 여러분이 Docker를 사용하고 있다면, DIGITS에서 사용할 수 있는 메모리양을 304 | 늘릴 수 있습니다. (Docker에서, 환경설정 -*preferences*- -> 고급 -*preferences*- -> 메모리 -*preferences*- ) 305 | 306 | 처음엔, 우리 신경망의 정확도는 50% 미만입니다. 원래 이렇습니다. 처음에는 무작위로 할당된 가중치를 307 | 사용하여 두 카테고리 중 "추측"하는 것이기 때문입니다. 시간이 지남에 따라 0.37의 loss로 87.5%의 308 | 정확도를 달성할 수 있습니다. 전체 30 epoch까지 전 6분도 채 걸리지 않았습니다. 309 | 310 | ![Model Attempt 1](images/model-attempt1.png?raw=true "Model Attempt 1") 311 | 312 | 우리가 업로드한 이미지나 웹상의 이미지에 URL을 사용하여 우리의 모델을 테스트할 수 있습니다. 313 | 훈련/검증 데이터셋에 없는 몇 가지 예제를 통해 테스트해 보겠습니다. 314 | 315 | ![Model 1 Classify 1](images/model-attempt1-classify1.png?raw=true "Model 1 Classify 1") 316 | 317 | ![Model 1 Classify 2](images/model-attempt1-classify2.png?raw=true "Model 1 Classify 2") 318 | 319 | 거의 완벽해 보입니다. 다음 시도를 하기 전까지는 말이죠: 320 | 321 | ![Model 1 Classify 3](images/model-attempt1-classify3.png?raw=true "Model 1 Classify 3") 322 | 323 | 여기서 완전히 실패합니다. 해마를 돌고래로 착각하는데, 최악은 높은 자신감으로 해마라고 판별하는 것입니다. 324 | 325 | 현실은 우리의 데이터셋이 너무 작아 정말 좋은 신경망을 훈련시키는 데에는 쓸만 하지 않다는 것입니다. 326 | 모든 것을 처리하기 위해선 높은 연산능력과 10초에서 100초 정도의 수천 개의 이미지가 필요합니다. 327 | 328 | ### 훈련: 시도 2, AlexNet Fine Tuning 329 | 330 | #### Fine Tuning 하는 법 331 | 332 | 신경망을 처음부터 설계하고, 훈련하기에 충분한 데이터(e.g. 수백만의 이미지)를 수집하고, 333 | 훈련을 완료하기 위해 몇 주 동안 GPU에 엑세스하는 것은 우리가 하기엔 벅찹니다. 더 적은 334 | 양의 데이터로도 사용될 수 있도록 우리는 **Transfer Learning** 또는 **Fine Tuning**이라는 335 | 기술을 사용할 것입니다. Fine Tuning은 심층 신경망의 레이아웃을 활용하고 사전훈련된 신경망을 336 | 이용해 첫 번째 객체 감지 작업을 수행합니다. 337 | 338 | 쌍안경으로 멀리 있는 것을 보는 것처럼 신경망을 사용하는 것을 상상해보십시오. 먼저, 339 | 쌍안경을 눈에 대보면 모든 게 흐릿해집니다. 초점을 맞추면, 색깔, 선, 모양이 보이기 340 | 시작하고 마지막엔 새의 형태를 인식할 수 있게 됩니다. 그리고 조금 더 조정한다면 새의 341 | 종까지 구분해낼 수 있게 됩니다. 342 | 343 | 다중 계층 신경망에서, 초기 계층은 특징(e.g. 가장자리)을 추출하고, 다음 계층은 형태 344 | (e.g. 바퀴, 눈)를 알아내기 위해 이러한 특징들을 사용합니다. 즉, 이전 계층들의 누적된 특성을 345 | 기반으로 각각의 항목들을 분류하는 최종 분류 계층에 반영됩니다(e.g. 고양이 vs. 개). 신경망은 346 | 픽셀 단위에서 직사각형으로, 다리로, 특정 방향으로 걷는 두 개의 다리까지 인식할 수 347 | 있어야 하며, 마지막엔 이미지가 고양이를 가리킨다는 결론을 내릴 수 있어야 합니다. 348 | 349 | 우리가 하고자 하는 것은 기존에 훈련되어있던 이미지 클래스 대신 새로운 이미지 클래스 세트로 350 | 분류하기 위해 사전훈련된 기존 신경망을 전문적으로 다루는 것입니다. 신경망은 이미 이미지의 351 | 특징을 "인식"하는 법을 알고 있음으로 특정한 이미지 형태로 "인식"하기 위해 우리가 신경망을 352 | 재훈련하고자 합니다. 계층들의 대부분은 처음부터 시작할 필요가 없습니다--이런 계층에서 이미 353 | 수행했던 학습을 새로운 분류 작업으로 이전하고자 합니다. 랜덤한 가중치를 사용했던 이전 354 | 시도와는 달리, 우리는 최종 신경망의 기존 가중치를 훈련하는 데 사용할 것입니다. 그러나 355 | 우리는 최종 분류 계층을 버리고, *우리의* 이미지 데이터셋을 사용해 신경망을 재교육하여 356 | 이미지 클래스에 맞게 미세 조정 -*fine tuning*- 할 것입니다. 357 | 358 | 이것이 실행되기 위해서는 학습된 가중치가 쓸만할 만큼 우리의 데이터와 충분히 비슷한 결과가 나오는 359 | 사전훈련된 신경망이 필요합니다. 다행히도 우리가 아래에서 사용할 신경망은 [ImageNet](http://image-net.org/) 360 | 의 수백만 개의 자연 이미지로 훈련되었으며, 광범위한 분류 작업에 뛰어난 성능을 보입니다. 361 | 362 | 이 테크닉은 의학 이미지에서 눈병을 검사하고, 바다에서 수집한 현미경 이미지에서 플랑크톤 종을 363 | 식별하며, Flickr 이미지의 미술 양식을 분류하는 것과 같은 흥미로운 일들을 하는 데 사용되어 364 | 왔습니다. 365 | 366 | 모든 머신러닝과 마찬가지로 이 작업을 완벽하게 수행하려면 데이터 및 신경망 아키텍처를 이해해야 367 | 합니다--데이터의 과적합에 주의해야 하며 일부 계층을 수정해야 하거나 새 계층을 삽입해야 하는 368 | 경우도 있습니다. 하지만, 제 경험상, 대부분의 경우에 "단지 작동"할 뿐이며 그저 경험을 쌓고 369 | 우리의 단순한 접근법을 사용하여 무엇을 달성할 수 있는지 확인하는 것만으로 가치 있습니다. 370 | 371 | #### 사전훈련된 신경망 업로드 372 | 373 | 첫 번째 시도에서는 Alexnet의 아키텍처를 사용했지만, 신경망 계층에서 랜덤한 가중치로 시작했습니다. 374 | 우리는 대규모 데이터셋에 대해 이미 훈련받은 버전의 AlexNet을 다운로드하고 사용하고자 합니다. 375 | 376 | 다행히도 우리는 이것을 바로 할 수 있습니다. AlexNet의 스냅샷은 여기서 다운로드할 수 있습니다: https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet. 377 | 우리는 훈련된 가중치를 포함하고 있는 이진 파일 `.caffemodel` 도 필요하고, 그것은 http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel 에서 다운로드할 수 있습니다. 378 | 379 | 사전훈련된 모델을 받는 동안, 하나 더 해봅시다. 380 | 2014년에 Google은 [GoogLeNet](https://research.google.com/pubs/pub43022.html)으로 같은 381 | ImageNet 대회에서 우승했습니다(코드명 Inception): 382 | 22계층의 신경망, GoogLeNet의 스냅샷도 다운로드할 수 있습니다. https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenet 을 참조하십시오. 383 | 다시 말하지만, 우리는 http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel 에서 다운로드 384 | 할 수 있는 모든 사전훈련된 가중치들로 구성된 `.caffemodel` 파일이 필요합니다. 385 | 386 | 우리는 `.caffemodel` 파일을 가지고 DIGITs에 업로드할 수 있습니다. DIGITS 홈페이지에 387 | **Pretrained Models** 탭으로 이동하여 **Upload Pretrained Model**을 클릭합니다: 388 | 389 | ![Load Pretrained Model](images/load-pretrained-model.png?raw=true "Load Pretrained Model") 390 | 391 | 이러한 사전훈련된 두 모델은 모두 DIGITS가 제공하는 기본설정값을 사용할 수 있습니다(i.e. 392 | 256 x 256의 스쿼시 된 컬러 이미지). 우리는 `가중치 -Weights- (**.caffemodel)` 393 | 및 ` 모델 정의 -Model Definition- (original.prototxt)`만 제공하면 됩니다. 394 | 각 버튼을 클릭하여 파일을 선택하십시오. 395 | 396 | 모델 정의(model definitions)에 대해서는 GoogLeNet의 경우에는 https://github.com/BVLC/caffe/blob/master/models/bvlc_googlenet/train_val.prototxt 을 397 | 참조하고 AlexNet의 경우에는 https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt 398 | 을 참조할 수 있습니다. 우리는 이러한 분류 레이블들을 사용하지 않을 것이므로 `labels.txt` 파일 추가는 399 | 생략하겠습니다: 400 | 401 | ![Upload Pretrained Model](images/upload-pretrained-model.png?raw=true "Upload Pretrained Model") 402 | 403 | 다음 단계에서 AlexNet과 GoogLeNet을 모두 사용할 것이므로 이 과정을 반복하십시오. 404 | 405 | > Q: "미세 조정 -fine tuning- 의 기반으로 적합한 다른 신경망이 있을까요?" 406 | 407 | [Caffe Model Zoo](http://caffe.berkeleyvision.org/model_zoo.html) 는 다른 사전훈련된 408 | 신경망들을 꽤 많이 가지고 있습니다. https://github.com/BVLC/caffe/wiki/Model-Zoo 을 409 | 참조하십시오. 410 | 411 | #### 돌고래와 해마로 AlexNet을 미세 조정하기 -Fine Tuning- 412 | 413 | 사전훈련된 Caffe 모델을 사용하여 신경망을 훈련하는 것은 몇 가지 조정을 해야 하지만, 처음부터 414 | 시작하는 것과 비슷합니다. 먼저, 이렇게 크게 변화할 필요가 없음므로(즉, *미세*하게 조정 415 | 중입니다.) **기본 학습 속도 -Base Learning Rate-** 를 0.01에서 0.001로 조정합니다. 우리는 416 | 또한 **사전훈련된 신경망 -Pretrained Network-** 을 사용하여 **커스터마이징 -Customize-** 할 417 | 것입니다. 418 | 419 | ![New Image Classification](images/new-image-classification-model-attempt2.png?raw=true "New Image Classification") 420 | 421 | 사전훈련된 모델의 정의(i.e. prototext)에서는 모든 참조의 이름을 **완전히 연결된 계층-*Fully Connected Layer*-**(최종 422 | 결과 분류가 이루어지는 곳)으로 변경해야 합니다. 모델이 원래의 훈련 데이터와 비교해 새로운 423 | 카테고리를 다시 학습하기를 원하기 때문입니다(즉, 현재의 마지막 계층은 폐기하고자 합니다). 424 | 우리는 최종적으로 완전히 연결된 계층-*fully connected layer*-의 이름을 변경해야만 합니다. 425 | 예를 들면, "fc8"에서 "fc9"로 말입니다. 마지막으로, 우리는 또한 `num_output`을 `2`로 변경하여, 426 | 카테고리 수를 `1000`에서 `2`로 조정해야 합니다. 427 | 428 | 여기 우리가 변경해야 할 사항이 있습니다: 429 | 430 | ```diff 431 | @@ -332,8 +332,8 @@ 432 | } 433 | layer { 434 | - name: "fc8" 435 | + name: "fc9" 436 | type: "InnerProduct" 437 | bottom: "fc7" 438 | - top: "fc8" 439 | + top: "fc9" 440 | param { 441 | lr_mult: 1 442 | @@ -345,5 +345,5 @@ 443 | } 444 | inner_product_param { 445 | - num_output: 1000 446 | + num_output: 2 447 | weight_filler { 448 | type: "gaussian" 449 | @@ -359,5 +359,5 @@ 450 | name: "accuracy" 451 | type: "Accuracy" 452 | - bottom: "fc8" 453 | + bottom: "fc9" 454 | bottom: "label" 455 | top: "accuracy" 456 | @@ -367,5 +367,5 @@ 457 | name: "loss" 458 | type: "SoftmaxWithLoss" 459 | - bottom: "fc8" 460 | + bottom: "fc9" 461 | bottom: "label" 462 | top: "loss" 463 | @@ -375,5 +375,5 @@ 464 | name: "softmax" 465 | type: "Softmax" 466 | - bottom: "fc8" 467 | + bottom: "fc9" 468 | top: "softmax" 469 | include { stage: "deploy" } 470 | ``` 471 | 472 | 제가 사용하고 있는 완전히 수정된 파일을 [src/alexnet-customized.prototxt](src/alexnet-customized.prototxt) 473 | 에 포함했습니다. 474 | 475 | 이번에는 정확도가 60%에서 시작해 87.5%로 급등하며 96%까지 이윽고 100%까지 상승하며, 476 | 손실 *Loss* 은 꾸준히 감소했습니다. 5분이 지나면 100%의 정확도와 0.0009의 손실이 발생합니다. 477 | 478 | ![Model Attempt 2](images/model-attempt2.png?raw=true "Model Attempt 2") 479 | 480 | 이전 신경망이 오류를 일으킨 것과 같은 해마 이미지를 테스트한 결과, 우리는 완전한 반전을 481 | 볼 수 있습니다: 100% 해마. 482 | 483 | ![Model 2 Classify 1](images/model-attempt2-classify1.png?raw=true "Model 2 Classify 1") 484 | 485 | 심지어 어린이들이 그린 해마 이미지에도 효과가 있습니다: 486 | 487 | ![Model 2 Classify 2](images/model-attempt2-classify2.png?raw=true "Model 2 Classify 2") 488 | 489 | 돌고래도 마찬가지입니다: 490 | 491 | ![Model 2 Classify 3](images/model-attempt2-classify3.png?raw=true "Model 2 Classify 3") 492 | 493 | 이처럼 여러 마리의 돌고래들이 서로 가까이 붙어 있고, 그들의 몸 대부분이 물속에 잠겨 있어 식별하기에 494 | 어려워 보이는 이미지들임에도 불구하고, 잘 작동됩니다: 495 | 496 | ![Model 2 Classify 4](images/model-attempt2-classify4.png?raw=true "Model 2 Classify 4") 497 | 498 | ### 훈련: 시도 3, GoogLeNet 미세 조정-*Fine Tuning*- 499 | 500 | 우리가 미세 조정-*Fine Tuning*-을 위해 사용했던 이전의 AlexNet 모델과 마찬가지로, GoogLeNet도 501 | 사용할 수 있습니다. 신경망을 수정하는 것은 하나의 계층이 아니라 3개의 완전히 연결된 계층을 502 | 재정의해야 하므로 좀 더 까다롭습니다. 503 | 504 | 우리의 유스케이스에 맞게 GoogLeNet을 미세 조정하려면, 우리는 또다시 새로운 **분류 모델-*Classification Model*-** 505 | 을 만들어야 합니다: 506 | 507 | ![New Classification Model](images/new-image-classification-model-attempt3.png?raw=true "New Classification Model") 508 | 509 | 완전히 연결된 세 가지 분류 계층인 `loss1/classifier`, `loss2/classifier`, `loss3/classifier`의 510 | 모든 참조의 이름을 변경하고 카테고리 수를 재정의합니다(`num_output: 2`). 여기에 3개의 분류 계층의 511 | 이름을 변경하고 카테고리 수를 1,000개에서 2개로 변경하기 위해 해야 할 사항들이 있습니다: 512 | 513 | ```diff 514 | @@ -917,10 +917,10 @@ 515 | exclude { stage: "deploy" } 516 | } 517 | layer { 518 | - name: "loss1/classifier" 519 | + name: "loss1a/classifier" 520 | type: "InnerProduct" 521 | bottom: "loss1/fc" 522 | - top: "loss1/classifier" 523 | + top: "loss1a/classifier" 524 | param { 525 | lr_mult: 1 526 | decay_mult: 1 527 | @@ -930,7 +930,7 @@ 528 | decay_mult: 0 529 | } 530 | inner_product_param { 531 | - num_output: 1000 532 | + num_output: 2 533 | weight_filler { 534 | type: "xavier" 535 | std: 0.0009765625 536 | @@ -945,7 +945,7 @@ 537 | layer { 538 | name: "loss1/loss" 539 | type: "SoftmaxWithLoss" 540 | - bottom: "loss1/classifier" 541 | + bottom: "loss1a/classifier" 542 | bottom: "label" 543 | top: "loss1/loss" 544 | loss_weight: 0.3 545 | @@ -954,7 +954,7 @@ 546 | layer { 547 | name: "loss1/top-1" 548 | type: "Accuracy" 549 | - bottom: "loss1/classifier" 550 | + bottom: "loss1a/classifier" 551 | bottom: "label" 552 | top: "loss1/accuracy" 553 | include { stage: "val" } 554 | @@ -962,7 +962,7 @@ 555 | layer { 556 | name: "loss1/top-5" 557 | type: "Accuracy" 558 | - bottom: "loss1/classifier" 559 | + bottom: "loss1a/classifier" 560 | bottom: "label" 561 | top: "loss1/accuracy-top5" 562 | include { stage: "val" } 563 | @@ -1705,10 +1705,10 @@ 564 | exclude { stage: "deploy" } 565 | } 566 | layer { 567 | - name: "loss2/classifier" 568 | + name: "loss2a/classifier" 569 | type: "InnerProduct" 570 | bottom: "loss2/fc" 571 | - top: "loss2/classifier" 572 | + top: "loss2a/classifier" 573 | param { 574 | lr_mult: 1 575 | decay_mult: 1 576 | @@ -1718,7 +1718,7 @@ 577 | decay_mult: 0 578 | } 579 | inner_product_param { 580 | - num_output: 1000 581 | + num_output: 2 582 | weight_filler { 583 | type: "xavier" 584 | std: 0.0009765625 585 | @@ -1733,7 +1733,7 @@ 586 | layer { 587 | name: "loss2/loss" 588 | type: "SoftmaxWithLoss" 589 | - bottom: "loss2/classifier" 590 | + bottom: "loss2a/classifier" 591 | bottom: "label" 592 | top: "loss2/loss" 593 | loss_weight: 0.3 594 | @@ -1742,7 +1742,7 @@ 595 | layer { 596 | name: "loss2/top-1" 597 | type: "Accuracy" 598 | - bottom: "loss2/classifier" 599 | + bottom: "loss2a/classifier" 600 | bottom: "label" 601 | top: "loss2/accuracy" 602 | include { stage: "val" } 603 | @@ -1750,7 +1750,7 @@ 604 | layer { 605 | name: "loss2/top-5" 606 | type: "Accuracy" 607 | - bottom: "loss2/classifier" 608 | + bottom: "loss2a/classifier" 609 | bottom: "label" 610 | top: "loss2/accuracy-top5" 611 | include { stage: "val" } 612 | @@ -2435,10 +2435,10 @@ 613 | } 614 | } 615 | layer { 616 | - name: "loss3/classifier" 617 | + name: "loss3a/classifier" 618 | type: "InnerProduct" 619 | bottom: "pool5/7x7_s1" 620 | - top: "loss3/classifier" 621 | + top: "loss3a/classifier" 622 | param { 623 | lr_mult: 1 624 | decay_mult: 1 625 | @@ -2448,7 +2448,7 @@ 626 | decay_mult: 0 627 | } 628 | inner_product_param { 629 | - num_output: 1000 630 | + num_output: 2 631 | weight_filler { 632 | type: "xavier" 633 | } 634 | @@ -2461,7 +2461,7 @@ 635 | layer { 636 | name: "loss3/loss" 637 | type: "SoftmaxWithLoss" 638 | - bottom: "loss3/classifier" 639 | + bottom: "loss3a/classifier" 640 | bottom: "label" 641 | top: "loss" 642 | loss_weight: 1 643 | @@ -2470,7 +2470,7 @@ 644 | layer { 645 | name: "loss3/top-1" 646 | type: "Accuracy" 647 | - bottom: "loss3/classifier" 648 | + bottom: "loss3a/classifier" 649 | bottom: "label" 650 | top: "accuracy" 651 | include { stage: "val" } 652 | @@ -2478,7 +2478,7 @@ 653 | layer { 654 | name: "loss3/top-5" 655 | type: "Accuracy" 656 | - bottom: "loss3/classifier" 657 | + bottom: "loss3a/classifier" 658 | bottom: "label" 659 | top: "accuracy-top5" 660 | include { stage: "val" } 661 | @@ -2489,7 +2489,7 @@ 662 | layer { 663 | name: "softmax" 664 | type: "Softmax" 665 | - bottom: "loss3/classifier" 666 | + bottom: "loss3a/classifier" 667 | top: "softmax" 668 | include { stage: "deploy" } 669 | } 670 | ``` 671 | 672 | 전체파일을 [src/googlenet-customized.prototxt](src/googlenet-customized.prototxt)에 저장했습니다. 673 | 674 | > Q: "이러한 신경망의 prototext 정의를 변경하는 건 어떻게 하나요? 675 | > 우리는 완전히 연결된 계층의 이름과 카테고리의 수를 변경해보았습니다. 676 | > 그 밖에 어떤 것이 변경될 수 있으며 어떤 상황에서 변경되어야 하나요? 677 | 678 | 좋은 질문입니다. 그건 저도 궁금한 것입니다. 예를 들어, 저는 가중치가 변하지 않도록 [특정 계층을 "수정"](https://github.com/BVLC/caffe/wiki/Fine-Tuning-or-Training-Certain-Layers-Exclusively) 679 | 할 수 있다는 것을 알고 있습니다. 그 밖에 다른 것들을 하는 것은 계층들이 어떻게 작동하는지 680 | 이해해야 합니다. 이것은 이 안내서를 넘어선 일이고, 지금의 저자도 넘어서는 것입니다! 681 | 682 | 앞에서 했던 AlexNet 미세 조정과 마찬가지로, 학습률을 `0.01`에서 `0.001`로 10% 낮춥니다. 683 | 684 | > Q: "이러한 신경망을 미세 조정할 때 그 외에 어떤 변경이 의미가 있나요? 685 | > 다른 epoch 수, batch size, 솔버 유형 (Adam, AdaDelta, AdaGrad 등), 학습률, 정책 686 | > (Exponential Decay, Inverse Decay, Sigmoid Decay 등), 단계 크기, 감마 값은 어떻나요?" 687 | 688 | 좋은 질문이고 마찬가지로 저도 궁금한 것들입니다. 저는 이것들에 대해 막연하게 이해하고 있으며, 689 | 훈련 시 이러한 값들을 어떤 식으로 변경해야 할지 안다면 개선할 수 있을 것입니다. 물론 이보다 690 | 더 좋은 문서가 필요할 것입니다. 691 | 692 | GoogLeNet은 architecture보다 더 복잡한 아키텍처이므로 미세 조정에 더 많은 시간이 필요합니다. 693 | 제 노트북에서는 데이터셋으로 GoogLeNet을 재훈련시키는 데 10분이 소요되어 100% 정확도와 0.0070의 694 | 손실을 달성했습니다: 695 | 696 | ![Model Attempt 3](images/model-attempt3.png?raw=true "Model Attempt 3") 697 | 698 | AlexNet의 미세 조정에서 살펴본 것처럼, 수정된 GoogLeNet은 잘 작동합니다--지금까지 중 가장 뛰어난 성능입니다: 699 | 700 | ![Model Attempt 3 Classify 1](images/model-attempt3-classify1.png?raw=true "Model Attempt 3 Classify 1") 701 | 702 | ![Model Attempt 3 Classify 2](images/model-attempt3-classify2.png?raw=true "Model Attempt 3 Classify 2") 703 | 704 | ![Model Attempt 3 Classify 3](images/model-attempt3-classify3.png?raw=true "Model Attempt 3 Classify 3") 705 | 706 | ## 모델 사용 707 | 708 | 신경망을 훈련하고 테스트하였으니, 이제 다운받아 사용할 시간입니다. DIGITS로 훈련한 각 모델은 **Download Model** 버튼과 훈련 실행 중 서로 다른 스냅샷을 선택하는 방법이 있습니다(e.g. `Epoch #30`): 709 | 710 | ![Trained Models](images/trained-models.png?raw=true "Trained Models") 711 | 712 | **Download Model** 를 클릭하면 다음 파일들이 압축된 `tar.gz` 파일이 다운로드됩니다: 713 | 714 | ``` 715 | deploy.prototxt 716 | mean.binaryproto 717 | solver.prototxt 718 | info.json 719 | original.prototxt 720 | labels.txt 721 | snapshot_iter_90.caffemodel 722 | train_val.prototxt 723 | ``` 724 | 725 | Caffe 문서에 우리가 방금 만든 모델의 사용법에 대한 [멋진 설명](https://github.com/BVLC/caffe/wiki/Using-a-Trained-Network:-Deploy) 726 | 이 있습니다. 다음과 같이 쓰여 있습니다: 727 | 728 | > 신경망은 구조(.prototxt)와 가중치로(.caffemodel) 정의됩니다. 신경망이 훈련될 때 729 | > 가중치의 현재 상태-*current state*-는 .caffemodel에 저장됩니다. 이 두 가지를 통해 730 | > 우리는 훈련/테스트 단계에서 생산-*production*- 단계로 이동할 수 있습니다. 731 | > 732 | > 현재 상태로서는 신경망의 구조는 배포용으로 설계되어있지 않습니다. 신경망을 제품으로 733 | > 출시하기 전에 몇 가지 방법으로 신경망을 수정해야 합니다: 734 | > 735 | > 1. 분류-*classification*-에 관해서 데이터의 레이블을 더는 제공하지 않음으로 훈련에 사용된 데이터 계층을 제거하십시오. 736 | > 2. 데이터 레이블에 종속된 계층을 제거하십시오. 737 | > 3. 데이터를 수신하도록 신경망을 설정하십시오. 738 | > 4. 신경망이 결과를 출력하게 하십시오. 739 | 740 | DIGITS는 `prototxt` 파일의 각각 다른 버전들을 구분하여 이미 할 일을 끝냈습니다. 741 | 신경망을 사용할 때 주의해야 할 파일: 742 | 743 | * `deploy.prototxt` - 이미지 입력 데이터를 받아들일 준비가 된 신경망의 정의 744 | * `mean.binaryproto` - 모델이 처리하는 각각의 이미지에서 빼야할 이미지가 있는데, 그 빼야할 이미지를 말한다. 745 | * `labels.txt` - 출력하고자 하는 레이블 (`dolphin`, `seahorse`)과 카테고리 번호만 출력하는 경우를 위한 목록 746 | * `snapshot_iter_90.caffemodel` - 이것들은 우리 신경망을 위해 훈련된 가중치들이다. 747 | 748 | 우리는 이 파일들을 새로운 이미지로 분류하기 위해 다양한 방법들을 사용할 수 있습니다. 예를 들어, 749 | `CAFFE_ROOT`에서는 `build/examples/cpp_classification/classification.bin`을 사용해 하나의 750 | 이미지를 분류할 수 있습니다: 751 | 752 | ```bash 753 | $ cd $CAFFE_ROOT/build/examples/cpp_classification 754 | $ ./classification.bin deploy.prototxt snapshot_iter_90.caffemodel mean.binaryproto labels.txt dolphin1.jpg 755 | ``` 756 | 757 | 이러면 디버그 텍스트 다발을 뱉어내고, 이어서는 두 카테고리에 대한 예측이 뒤따를 것입니다: 758 | 759 | ``` 760 | 0.9997 - “dolphin” 761 | 0.0003 - “seahorse” 762 | ``` 763 | 764 | [전체 C++ 소스](https://github.com/BVLC/caffe/tree/master/examples/cpp_classification)는 765 | [Caffe 예제들](https://github.com/BVLC/caffe/tree/master/examples)에서 확인할 수 있습니다. 766 | 767 | Python 인터페이스를 사용하는 분류 버전의 경우, DIGITS에 [좋은 예제](https://github.com/NVIDIA/DIGITS/tree/master/examples/classification) 768 | 가 있습니다.또한 Caffe 예제들 안에는 [꽤 잘 문서화된 파이썬 워크스루](https://github.com/BVLC/caffe/blob/master/examples/00-classification.ipynb) 769 | 도 있습니다. 770 | 771 | ### 파이썬 예제 772 | 773 | 미세 조정된 GoogLeNet 모델을 사용하여 [data/untrained-samples](data/untrained-samples)에 있는 774 | 훈련되지 않은 이미지를 분류하는 프로그램을 작성합시다. 위의 예제들과 `caffe` [Python module's source](https://github.com/BVLC/caffe/tree/master/python) 775 | 를 바탕으로 종합해보았습니다. 여러분은 이제부터 알려드릴 것을 좋아하실 겁니다. 776 | 777 | 제가 말하고자 하는 내용의 전체 버전은 [src/classify-samples.py](src/classify-samples.py)에서 확인하실 수 있습니다. 778 | 시작하겠습니다. 779 | 780 | 먼저, [NumPy](http://www.numpy.org/) 모듈이 필요합니다. 잠시 후에 [NumPy](http://www.numpy.org/) 781 | 를 이용해 Caffe에서 많이 쓰이는 [`ndarray`s](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html) 782 | 를 사용할 것입니다. 안 써보셨다면, 저도 안 써봤지만, 이 [퀵스타트 튜토리얼](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html) 783 | 을 읽어보시는 건 어떨까요? 784 | 785 | 두 번째로 우리는 `CAFFE_ROOT` 디렉터리로부터 `caffe` 모듈을 적재해야 합니다. 파이썬 환경에 포함되어 786 | 있지 않으면 수동으로 추가하여 강제로 적재할 수 있습니다. 마찬가지로 우리는 caffe의 protobuf 787 | 모듈도 가져와야 합니다: 788 | 789 | ```python 790 | import numpy as np 791 | 792 | caffe_root = '/path/to/your/caffe_root' 793 | sys.path.insert(0, os.path.join(caffe_root, 'python')) 794 | import caffe 795 | from caffe.proto import caffe_pb2 796 | ``` 797 | 798 | 다음으로 [CPU 또는 GPU](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L50-L52) 799 | 중 어떤 것을 사용하는지 Caffe에 알려야 합니다. 800 | 실험에서 CPU는 잘 작동합니다: 801 | 802 | ```python 803 | caffe.set_mode_cpu() 804 | ``` 805 | 806 | 이제 우리는 `caffe`에서 훈련된 신경망을 적재해보겠습니다. 그러려면 DIGITS에서 다운로드한 807 | 파일 중 일부가 필요합니다: 808 | 809 | * `deploy.prototxt` - our "신경망 파일", 신경망에 대한 서술. 810 | * `snapshot_iter_90.caffemodel` - 훈련된 "가중치" 811 | 812 | 분명하게 전체 경로를 제공해줘야 하며, 전 제 파일들이 `model/` 디렉터리에 있다고 가정하겠습니다: 813 | 814 | ```python 815 | model_dir = 'model' 816 | deploy_file = os.path.join(model_dir, 'deploy.prototxt') 817 | weights_file = os.path.join(model_dir, 'snapshot_iter_90.caffemodel') 818 | net = caffe.Net(deploy_file, caffe.TEST, weights=weights_file) 819 | ``` 820 | 821 | `caffe.Net()` [생성자](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L91-L117) 822 | 는 신경망 파일, 단계-*phase*-(`caffe.TEST` 또는 `caffe.TRAIN`) 및 부가 가중치 파일 이름을 823 | 사용합니다. 우리가 가중치 파일을 주면, `Net`이 자동으로 적재합니다. `Net`에는 여러분이 사용할 824 | 수 있는 몇 가지 [method와 attribute](https://github.com/BVLC/caffe/blob/master/python/caffe/pycaffe.py) 825 | 가 있습니다. 826 | 827 | **Note:** [생성자의 deprecated version](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L119-L134), 828 | 도 있으며, 웹상에 샘플 코드에서 자주 사용되어 집니다. 이렇게 생겼습니다: 829 | 830 | ```python 831 | net = caffe.Net(str(deploy_file), str(model_file), caffe.TEST) 832 | ``` 833 | 834 | 우리는 테스트를 위해 다양한 크기의 이미지를 신경망에 업로드하는 데 관심 있습니다. 따라서, 신경망에서 835 | 사용할 수 있는 형태(i.e. 컬러, 256x256)로 *변형*해야 하는데, 이를 위해 Caffe에서 [`Transformer` 클래스](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L98) 836 | 를 제공하고 있습니다. 우리는 이것을 이미지/신경망에 알맞게 변형하기 위해 사용할 것입니다: 837 | 838 | ```python 839 | transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) 840 | # set_transpose: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L187 841 | transformer.set_transpose('data', (2, 0, 1)) 842 | # set_raw_scale: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L221 843 | transformer.set_raw_scale('data', 255) 844 | # set_channel_swap: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L203 845 | transformer.set_channel_swap('data', (2, 1, 0)) 846 | ``` 847 | 848 | 또한 transformer의 mean을 설정하기 위해 DIGITS가 제공하는 `mean.binaryproto` 파일을 사용하겠습니다: 849 | 850 | ```python 851 | # This code for setting the mean from https://github.com/NVIDIA/DIGITS/tree/master/examples/classification 852 | mean_file = os.path.join(model_dir, 'mean.binaryproto') 853 | with open(mean_file, 'rb') as infile: 854 | blob = caffe_pb2.BlobProto() 855 | blob.MergeFromString(infile.read()) 856 | if blob.HasField('shape'): 857 | blob_dims = blob.shape 858 | assert len(blob_dims) == 4, 'Shape should have 4 dimensions - shape is %s' % blob.shape 859 | elif blob.HasField('num') and blob.HasField('channels') and \ 860 | blob.HasField('height') and blob.HasField('width'): 861 | blob_dims = (blob.num, blob.channels, blob.height, blob.width) 862 | else: 863 | raise ValueError('blob does not provide shape or 4d dimensions') 864 | pixel = np.reshape(blob.data, blob_dims[1:]).mean(1).mean(1) 865 | transformer.set_mean('data', pixel) 866 | ``` 867 | 868 | 레이블이 많이 있다면 레이블 파일-*labels file*-을 가져올 수도 있는데, 나중에 레이블의 상태-*position*- 869 | (e.g. 0=dolphin, 1=seahorse)를 사용하여 확률에 대해 레이블을 조회하고 사용할 수 있습니다: 870 | 871 | ```python 872 | labels_file = os.path.join(model_dir, 'labels.txt') 873 | labels = np.loadtxt(labels_file, str, delimiter='\n') 874 | ``` 875 | 876 | 이제 이미지를 분류하기 위한 준비를 마쳤습니다. [`caffe.io.load_image()`](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L279) 877 | 를 사용해 이미지 파일을 읽은 다음 transformer를 사용해 형태를 바꾸고 신경망의 데이터 계층으로 878 | 설정합니다: 879 | 880 | ```python 881 | # Load the image from disk using caffe's built-in I/O module 882 | image = caffe.io.load_image(fullpath) 883 | # Preprocess the image into the proper format for feeding into the model 884 | net.blobs['data'].data[...] = transformer.preprocess('data', image) 885 | ``` 886 | 887 | > Q: "파일 대신에, 카메라나 비디오 스트림의 이미지를 사용하려면 어떻게 해야 하나요?" 888 | 889 | 좋은 질문입니다. 여기 스켈레톤 코드가 있습니다: 890 | 891 | ```python 892 | import cv2 893 | ... 894 | # Get the shape of our input data layer, so we can resize the image 895 | input_shape = net.blobs['data'].data.shape 896 | ... 897 | webCamCap = cv2.VideoCapture(0) # could also be a URL, filename 898 | if webCamCap.isOpened(): 899 | rval, frame = webCamCap.read() 900 | else: 901 | rval = False 902 | 903 | while rval: 904 | rval, frame = webCamCap.read() 905 | net.blobs['data'].data[...] = transformer.preprocess('data', frame) 906 | ... 907 | 908 | webCamCap.release() 909 | ``` 910 | 911 | 다시 우리의 문제로 돌아가서, 우리는 다음으로 이미지 데이터로 신경망을 실행하고 레이블 912 | 카테고리별로 순서대로 정렬될 신경망의 마지막 `'softmax'` 계층에서 확률을 읽어야 합니다: 913 | 914 | ```python 915 | # Run the image's pixel data through the network 916 | out = net.forward() 917 | # Extract the probabilities of our two categories from the final layer 918 | softmax_layer = out['softmax'] 919 | # Here we're converting to Python types from ndarray floats 920 | dolphin_prob = softmax_layer.item(0) 921 | seahorse_prob = softmax_layer.item(1) 922 | 923 | # Print the results. I'm using labels just to show how it's done 924 | label = labels[0] if dolphin_prob > seahorse_prob else labels[1] 925 | filename = os.path.basename(fullpath) 926 | print '%s is a %s dolphin=%.3f%% seahorse=%.3f%%' % (filename, label, dolphin_prob*100, seahorse_prob*100) 927 | ``` 928 | 929 | [data/untrained-samples](data/untrained-samples) 이미지에서 미세 조정된 GoogLeNet 신경망을 사용하여 930 | 이것의([src/classify-samples.py](src/classify-samples.py)를 보십시오. 풀 버전을 실행한다면 다음과 같은 결과가 나옵니다: 931 | 932 | ``` 933 | [...truncated caffe network output...] 934 | dolphin1.jpg is a dolphin dolphin=99.968% seahorse=0.032% 935 | dolphin2.jpg is a dolphin dolphin=99.997% seahorse=0.003% 936 | dolphin3.jpg is a dolphin dolphin=99.943% seahorse=0.057% 937 | seahorse1.jpg is a seahorse dolphin=0.365% seahorse=99.635% 938 | seahorse2.jpg is a seahorse dolphin=0.000% seahorse=100.000% 939 | seahorse3.jpg is a seahorse dolphin=0.014% seahorse=99.986% 940 | ``` 941 | 942 | 저는 계속 코드상에서 모델로 작동하는 사례들을 살펴보며 공부하는 중입니다. 여기에 보여드릴 943 | 수 있는 코드 예시, API, free-made 모듈 등이 더 많이, 잘 문서화되면 좋겠습니다. 저도 제가 944 | 찾은 대부분의 코드 예제가 단순하고 어쩌면 형편없이 문서화되어 있다는 것을 알고 있습니다 945 | --특히 Caffe 문서는 지저분하고 많은 것을 가정해가며 했습니다. 946 | 947 | 제가 보기엔 초보자용 Caffe 인터페이스와 여기와 같은 기초적인 워크플로우 위에 더 높은 수준의 948 | 도구를 구축할 기회가 온 것 같습니다. 여러분에게 "잘했어"라고 알려줄 수 있는 고급 언어로 된 더 949 | 간단한 모듈들이 있다면 좋을 텐데 말입니다; 누군가는 이것을 받아들일 수 있고, DIGITS가 *훈련* 950 | 시키는 것만큼 쉽게 Caffe 모델을 사용할 수 있거나 사용해야 할 겁니다. Node.js같은 곳에서 951 | 사용할 수 있으면 좋을 텐데요. 이상적으로는 모델이나 Caffe 내부에 대해 많은 것을 알 필요는 952 | 없습니다. 아직 사용해본 적은 없지만, [DeepDetect](https://deepdetect.com/)도 이러한 면에서 953 | 흥미로워 보이고, 제가 알지 못하는 다른 많은 툴이 있을 수 있습니다. 954 | 955 | ## 결과 956 | 957 | 처음에 우리는 신경망을 이용하여 [data/untrained-samples](data/untrained-samples)의 모든 이미지를 958 | 올바르게 분류하는 프로그램을 작성하는 것이 목표라고 했었죠? 이건 훈련이나 검증 데이터로 쓰인 적 없는 959 | 돌고래와 해마의 이미지입니다: 960 | 961 | ### 훈련에 쓰인 적 없는 돌고래 이미지 962 | 963 | ![Dolphin 1](data/untrained-samples/dolphin1.jpg?raw=true "Dolphin 1") 964 | ![Dolphin 2](data/untrained-samples/dolphin2.jpg?raw=true "Dolphin 2") 965 | ![Dolphin 3](data/untrained-samples/dolphin3.jpg?raw=true "Dolphin 3") 966 | 967 | ### 훈련에 쓰인 적 없는 해마 이미지 968 | 969 | ![Seahorse 1](data/untrained-samples/seahorse1.jpg?raw=true "Seahorse 1") 970 | ![Seahorse 2](data/untrained-samples/seahorse2.jpg?raw=true "Seahorse 2") 971 | ![Seahorse 3](data/untrained-samples/seahorse3.jpg?raw=true "Seahorse 3") 972 | 973 | 이 과제를 해결하기 위한 세 가지 시도를 각각 살펴보겠습니다: 974 | 975 | ### 모델 시도 1: AlexNet, 처음부터 (3rd Place) 976 | 977 | | Image | Dolphin | Seahorse | Result | 978 | |-------|---------|----------|--------| 979 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 71.11% | 28.89% | :expressionless: | 980 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.2% | 0.8% | :sunglasses: | 981 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 63.3% | 36.7% | :confused: | 982 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 95.04% | 4.96% | :disappointed: | 983 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 56.64% | 43.36 | :confused: | 984 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 7.06% | 92.94% | :grin: | 985 | 986 | ### Model Attempt 2: AlexNet, 미세 조정 (2nd Place) 987 | 988 | | Image | Dolphin | Seahorse | Result | 989 | |-------|---------|----------|--------| 990 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.1% | 0.09% | :sunglasses: | 991 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.5% | 0.05% | :sunglasses: | 992 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 91.48% | 8.52% | :grin: | 993 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0% | 100% | :sunglasses: | 994 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 995 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0% | 100% | :sunglasses: | 996 | 997 | ### Model Attempt 3: GoogLeNet, 미세 조정 (1st Place) 998 | 999 | | Image | Dolphin | Seahorse | Result | 1000 | |-------|---------|----------|--------| 1001 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.86% | 0.14% | :sunglasses: | 1002 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 100% | 0% | :sunglasses: | 1003 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 100% | 0% | :sunglasses: | 1004 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0.5% | 99.5% | :sunglasses: | 1005 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 1006 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0.02% | 99.98% | :sunglasses: | 1007 | 1008 | ## 결론 1009 | 1010 | 우리가 만든 모델이 얼마나 잘 작동하는지, 사전훈련된 신경망을 미세 조정함으로써 무엇이 가능한지는 정말 1011 | 놀랍지 않나요? 물론 돌고래 vs 해마의 예제는 인위적이며 데이터셋이 지나치게 제한적이었습니다. 신경망이 1012 | 강력해지기 위해선 훨씬 더 많은 데이터가 필요합니다. 하지만 우리의 목표는 신경망의 툴과 워크플로우를 1013 | 조사하는 것이었기 때문에, 특히 비싼 장비나 많은 시간은 없었기 때문에 우리에겐 알맞은 케이스였습니다. 1014 | 1015 | 무엇보다 이 경험이 시작에 대한 벅찬 두려움을 없애는 데 도움이 되길 바랍니다. 머신러닝과 신경망의 이론을 1016 | 배우는 데 시간을 투자할 가치가 있는지를 결정하는 것은 간소화된 방식으로 볼 수 있을 때 더 쉬울 겁니다. 1017 | 이제 설정하는 법과 접근 방식을 알았으므로 다른 분류도 해볼 수 있을 것입니다. Caffe와 DIGITS로 1018 | 수행할 수 있는 다른 유형들도 살펴볼 수 있게 되었습니다. 예를 들어, 이미지 내의 개체를 찾거나 분리하는 1019 | 것 등입니다. 1020 | 1021 | 머신러닝을 즐겨보세요! 1022 | -------------------------------------------------------------------------------- /README_zh-tw.md: -------------------------------------------------------------------------------- 1 | # 機器學習動手玩:給新手的教學 2 | 3 | > Author: David Humphrey (original [English version](README.md)) 4 | 中文(繁體)語系譯者:[Birkhoff Lee](https://fb.me/birkhofflee) 5 | 6 | ### 譯者有話要說 Translator's Note 7 | 8 | 各位好。這是一篇很棒的教學,小弟希望能夠幫助到中文讀者,於是利用自己課後的時間(我還是個國中生)來翻譯這篇文章。在這篇文章裡有很多專業的術語,而有些是我不曾聽聞的——例如「Transfer Learning」——我遇到這些我不清楚的術語時,我使用 Google 來搜尋相關的中文文獻以期得到該術語現有的翻譯。還有一些內容是無法直接從英文翻到中文的,必須重建語境來翻譯,因此我會盡可能地不偏離原文的意思。如果您發現哪裡的翻譯有問題或是可以翻譯地更好,請開一個 issue 或是直接發一個 pull request 來協助修正翻譯,謝謝。 9 | 10 | ## 序言 11 | 12 | 這是一個提供給**無人工智慧背景知識**程式員的機器學習**實作教學**。使用類神經網絡事實上並不需要什麼博士學位,而且你也不需要成為下一個在人工智慧領域有極大突破的人,而且我們現在的成就已經十分驚人,且可用性極高。我相信大多數人是想玩玩看這個東西——就跟我們玩開源軟體一樣——而不是將它視為一個研究議題。 13 | 14 | 在這篇教學中,我們的目標是寫一個程式,能夠使用機器學習來進行精確的判定——僅僅依該圖片來判斷在 [data/untrained-samples](data/untrained-samples) 中的陌生圖案是**海豚**還是**海馬**。以下是兩個我們將會用到的範例圖案: 15 | 16 | ![一隻海豚](data/untrained-samples/dolphin1.jpg?raw=true "海豚") 17 | ![一隻海馬](data/untrained-samples/seahorse1.jpg?raw=true "海馬") 18 | 19 | 為了進行判定,我們將訓練一個[卷積神經網絡](https://zh.wikipedia.org/wiki/%E5%8D%B7%E7%A7%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C)。我們將以實踐者的角度(而不是以第一原理\* 的觀點)來解決這個問題。人工智慧現在十分令人興奮,不過現在大多數關於 AI 的文章就像是物理學家在黑板上傳授你騎腳踏車技巧一樣,不過你應該與你的朋友在公園內練習才對不是嗎? 20 | 21 | 我決定在 GitHub 上發表這篇文章,而不是在我的部落格上。因為我確定以下我所寫的內容有些會誤導讀者,或根本是錯的。我還在自學這一方面的知識,而且我發現一些新手教學會成為障礙。如果你發現我哪裡寫錯了、或是缺少了什麼重要的細節,請建立一個 pull request。 22 | 23 | 把那些拋諸腦後吧!讓我來教你如何使用這些東西。 24 | 25 | > \* 譯者按:「第一原理」是指不必經過驗證,即已明白的原理,即是理由已經存在於原理之中,也是自證原理。 26 | 就範圍大小區分,第一原理可以是解釋所有事件的終極真理,也可以視為一個系統一致性、連貫性的 27 | 一種根源性的解釋。 28 | 29 | ## 概覽 30 | 31 | 以下是我們將要探索的內容: 32 | 33 | * 設定並且使用現有且開放原始碼的機器學習技術,特別是 [Caffe](http://caffe.berkeleyvision.org/) 與 [DIGITS](https://developer.nvidia.com/digits)。 34 | * 建立一個圖像資料集 35 | * 從頭開始訓練一個類神經網絡 36 | * 用我們的類神經網絡測試判別它從沒見過的圖案 37 | * 對現有的類神經網絡(AlexNet 與 GoogLeNet)進行微調以改進我們類神經網絡的判別準確度 38 | * 部署並使用我們的類神經網絡 39 | 40 | 這個教學將不會教你這些類神經網絡是如何設計的、其背後的理論,也不會給你什麼數學表達式。我不假裝全然理解我接下來將教你的。相反地,我們將以有趣的方式來用現有的東西解決一個困難的問題。 41 | 42 | > 問:「我知道你說了我們不會討論類神經網絡背後的理論,但是我還是覺得在我們開始之前我至少需要一些概覽。我該從何開始?」 43 | 44 | 網路上大概有上百個關於這個東西的介紹。其中不乏短文、甚至完整的線上課程應有盡有。看你希望如何學習,這裡有三個不錯的選項供你參考: 45 | 46 | * 這個奇妙的[部落格文章](https://jalammar.github.io/visual-interactive-guide-basics-neural-networks/),由 J Alammar 所著。它以直觀的例子介紹了類神經網絡的概念。 47 | * 類似的,這部由 [Brandon Rohrer](https://www.youtube.com/channel/UCsBKTrp45lTfHa_p49I2AEQ) 所拍攝的 [介紹影片](https://www.youtube.com/watch?v=FmpDIaiMIeA) 是個很不錯的關於卷積神經網絡(我們將會使用它)的介紹。 48 | * 如果你想了解更多其背後的理論,我會推薦你[這本書](http://neuralnetworksanddeeplearning.com/chap1.html),它由 [Michael Nielsen](http://michaelnielsen.org/) 所著。 49 | 50 | ## 設定 51 | 52 | 根據你所使用的平台與作業系統版本,安裝我們將使用到的軟體(Caffe 與 DIGITS)可能會讓你感到十分挫敗。目前為止最簡單的方式是使用 Docker。以下我們將示範如何使用 Docker 來設定,以及如何用原生的方式來設定它。 53 | 54 | ### 方法 1a:原生安裝 Caffe 55 | 56 | 首先,我們將會使用到來自 Berkely Vision 及 Learning Center 的 [Caffe 深度學習框架](http://caffe.berkeleyvision.org/)(BSD 協議)。 57 | 58 | > 問:「等一下,為什麼要用 Caffe?為什麼不使用最近很多人都在討論的 TensorFlow?」 59 | 60 | 我們有很多很棒的選擇,而且你應該都稍微了解一下他們。[TensorFlow](https://www.tensorflow.org/) 是很不錯,而且你也應該玩玩看。不過,由於以下這些原因我選擇使用 Caffe: 61 | 62 | * 它專門用來解決電腦視覺相關的問題 63 | * 它支援 C++、Python,及[即將到來的 Node.js 支援](https://github.com/silklabs/node-caffe) 64 | * 它既快又穩定 65 | 66 | 不過最首要的原因是**你不需要寫任何程式碼**來使用它。你可以用說明的方式做任何事,或是用命令行工具(Caffe 使用結構化文字檔來定義網絡架構)。還有,你可以透過一些不錯的前端介面來使用 Caffe 以訓練及驗證你的網絡,這將變得十分簡單。我們將會使用 [nVidia 公司的 DIGITS](https://developer.nvidia.com/digits) 來當做我們的前端介面。 67 | 68 | 要安裝好 Caffe 有點費力。這裡有對於某些平台的[安裝指引](http://caffe.berkeleyvision.org/installation.html),及一些已經預先編譯好的 Docker 或 AWS 配置。 69 | 70 | **注意:** 當我在寫這篇時,我使用了這個非正式版本的 Caffe:https://github.com/BVLC/caffe/commit/5a201dd960840c319cefd9fa9e2a40d2c76ddd73 71 | 72 | 想在 Mac 上安裝它很容易讓你感到挫敗,在編譯 Caffe 時會有很多版本問題,我試了很多天,也找了很多教學,每次都有略微不同的問題。最後我找到了最接近的[這篇](https://gist.github.com/doctorpangloss/f8463bddce2a91b949639522ea1dcbe4)。我也推薦[最近發表的這篇](https://eddiesmo.wordpress.com/2016/12/20/how-to-set-up-caffe-environment-and-pycaffe-on-os-x-10-12-sierra/)文章,裡面也提到很多我看到的討論串。對於中文讀者來說,[BirkhoffLee](https://github.com/BirkhoffLee) 也推薦了他完整的[中文版教學](https://blog.birkhoff.me/macos-sierra-10-12-2-build-caffe),教你如何在 macOS Sierra 上編譯 Caffe。 73 | 74 | 將 Caffe 安裝好是到目前為止我們將做的最難的事情,這很好,因為你可能會認為人工智慧方面的問題會更難。如果你遇到問題,千萬不要放棄,這是值得的。如果要讓我再做一次,我不會直接在 Mac 上安裝它,而是在一台 Ubuntu 虛擬機器上安裝。如果你有問題,這裡是 [Caffe 使用者群組](https://groups.google.com/forum/#!forum/caffe-users),你可以在此提問。 75 | 76 | > 問:「訓練一個類神經網絡需不需要很好的硬體?如果我沒有很棒的 GPU 呢?」 77 | 78 | 事實上沒錯。訓練一個深層類神經網絡需要非常大量的預算能力和精力...前提是你要用非常大量的資料集從頭開始訓練。我們不會這樣做。我們的秘訣是用一個別人事先以上百小時訓練好的類神經網絡,然後我們再針對我們的資料集進行微調。下面的教學將會教你如何這樣做。簡單來說,下面我所做的事情,都是我在一臺一歲的 MacBook Pro 上做的(這台沒有很好的 GPU)。 79 | 80 | 順便說一下,因為我的 MacBook Pro 只有 Intel 整合繪圖處理器(即內建顯示核心),它沒有 nVidia 的 GPU,所以我決定使用 [Caffe 的 OpenCL 版本](https://github.com/BVLC/caffe/tree/opencl),而且它在我的筆電上跑的很不錯。 81 | 82 | 當你把 Caffe 搞定之後,你應該有,或能做這些東西: 83 | 84 | * 一個資料夾,裡面有你編譯好的 Caffe。如果你用了標準的方法來編譯它,裡面會有一個叫做「`build/`」的資料夾,它裡面有你跑 Caffe 所需要的所有東西,像是 Python 的綁定什麼的。那個包含 `build/` 的資料夾就是你的「`CAFFE_ROOT`」(我們等一下會用到這個)。 85 | * 執行 `make test && make runtest` 要能通過測試 86 | * 安裝完所有 Python 相依性套件之後(在 `python/` 內執行 `pip install -r requirements.txt`),執行 `make pycaffe && make pytest` 要能通過測試 87 | * 你也應該執行 `make distribute` 以建立一個含有所有必須的 header、binary 之類東西的可散佈版的 Caffe。 88 | 89 | 在我的機器上,我已經完整的編譯好 Caffe 了。我的 CAFFE_ROOT 裡面的基本結構看起來長這樣: 90 | 91 | ``` 92 | caffe/ 93 | build/ 94 | python/ 95 | lib/ 96 | tools/ 97 | caffe ← 這是我們主要使用的執行檔 98 | distribute/ 99 | python/ 100 | lib/ 101 | include/ 102 | bin/ 103 | proto/ 104 | ``` 105 | 106 | 現在,我們已經萬事俱全,可以訓練、測試我們的網絡以及為它編寫程式了。在下一節我們將為 Caffe 添加一個十分友好的網頁介面——「DIGITS」,這樣我們訓練及測試我們的網絡時將變得更簡單。 107 | 108 | ### 方法 1b:原生安裝 DIGITS 109 | 110 | nVidia 的[深度學習 GPU 訓練系統(DIGITS)](https://github.com/NVIDIA/DIGITS)是個 BSD 協議的 Python 網頁應用程式,專門用來訓練類神經網絡。雖然我們可以在命令行(或是自己寫程式)完成任何 DIGITS 對 Caffe 做的事,但是用 DIGITS 將讓我們更容易上手。我發現 DIGITS 的視覺化資料、即時圖表和其他類似的功能讓這一切都變得更有趣了。因為你還在實驗及嘗試學習,我非常推薦以 DIGITS 上手。 111 | 112 | https://github.com/NVIDIA/DIGITS/tree/master/docs 有一些十分不錯的文檔供你參考,裡面也有[安裝](https://github.com/NVIDIA/DIGITS/blob/master/docs/BuildDigits.md)、[設定](https://github.com/NVIDIA/DIGITS/blob/master/docs/Configuration.md)及[供你上手](https://github.com/NVIDIA/DIGITS/blob/master/docs/GettingStarted.md)的資料。我建議在你開始之前,先把所有東西都稍微看一遍,因為我並不是 DIGITS 的專家——我並不知道它能做的所有事情。如果你有什麼問題想問,公開的 [DIGITS 使用者群組](https://groups.google.com/forum/#!forum/digits-users)是一個不錯的地方。 113 | 114 | 要安裝且執行 DIGITS 有很多方法,有 Docker image、預先包裝好的 Linux 套件,或者你也可以自行編譯它。我使用的是 Mac,所以我選擇自行編譯它。 115 | 116 | **注意:** 當我在寫這篇時,我使用了這個非正式版本的 DIGITS:https://github.com/NVIDIA/DIGITS/commit/81be5131821ade454eb47352477015d7c09753d9 117 | 118 | DIGITS 很容易安裝,因為他就只是一堆 Python 腳本。你唯一需要告訴 DIGITS 的一件事就是你的 `CAFFE_ROOT` 在哪裡。你可以用環境變數搞定這件事,然後就可以啟動伺服器了: 119 | 120 | ```bash 121 | export CAFFE_ROOT=/path/to/caffe 122 | ./digits-devserver 123 | ``` 124 | 125 | 注意:在 Mac 上我在啟動伺服器時發生了一些問題——啟動伺服器的腳本直接默認了我的 Python 執行檔叫做 `python2`,但是我只有 `python2.7`。你可以建立一個到 `/usr/bin` 的符號連結或是修改 DIGITS 的啟動腳本來使用正確的 Python 執行檔。 126 | 127 | 當你啟動了伺服器之後,你可以透過你的網頁瀏覽器在這個網址做所有其他的事情(我們等下會做的事)了:http://localhost:5000。 128 | 129 | ### 方法 2:用 Docker 執行 Caffe 與 DIGITS 130 | 如果你還沒安裝 [Docker](https://www.docker.com/) 請先安裝它,接著執行以下指令來拉取與執行一個完整的 Caffe + DIGITS 容器。 131 | 132 | ```bash 133 | git clone https://github.com/humphd/have-fun-with-machine-learning 134 | docker run --name digits -d -p 8080:5000 -v $(pwd)/have-fun-with-machine-learning:/data/repo kaixhin/digits 135 | ``` 136 | 137 | 這樣容器就開始執行了,你可以打開你的瀏覽器然後打開 `http://localhost:8080`。所有在這個 repository 的資料都在容器內的 `/data/repo` 了。就這樣。你已經把 Caffe 與 DIGITS 搞定了。 138 | 139 | 如果你需要 shell access,請使用以下指令: 140 | 141 | ```bash 142 | docker exec -it digits /bin/bash 143 | ``` 144 | 145 | ## 訓練類神經網絡 146 | 147 | 訓練一個類神經網絡涉及到這些步驟: 148 | 149 | 1. 組合及準備一個分類好的照片的資料集 150 | 2. 定義這個類神經網絡的架構 151 | 3. 用準備好的資料集訓練及驗證這個網絡 152 | 153 | 我們將用三種方法做這件事以體現出從頭開始訓練與使用一個預先訓練好的網絡之間的差別,順便了解如何使用 AlexNet 與 GoogLeNet 這兩個相當受歡迎的預先訓練好的網絡,他們常常與 Caffe 和 DIGITS 搭配使用。 154 | 155 | 我們將使用一個包含了海豚與海馬的小資料集來嘗試訓練。我已經把我使用的照片放在了 [data/dolphins-and-seahorses](data/dolphins-and-seahorses)。你需要最少兩個分類,不過你可以有更多(有些你將會用到的網絡是以一千多個影像分類訓練而成的)。我們的目標是當我們給我們的網絡一個圖片,它能告訴我們他是隻海豚還是海馬。 156 | 157 | ### 準備資料集 158 | 159 | 要開始,最簡單的方法是將你的圖片分成這個分類好的資料夾樣式: 160 | 161 | ``` 162 | dolphins-and-seahorses/ 163 | dolphin/ 164 | image_0001.jpg 165 | image_0002.jpg 166 | image_0003.jpg 167 | ... 168 | seahorse/ 169 | image_0001.jpg 170 | image_0002.jpg 171 | image_0003.jpg 172 | ... 173 | ``` 174 | 175 | 這裡的每個資料夾都是一個我們想分類的類別(category),在裡面的每個圖片都將被我們用來訓練及驗證我們的網絡。 176 | 177 | > 問:「照片都要一樣的大小嗎?那檔案名稱呢?」 178 | 179 | 兩個都不用管他。在我們餵食網絡之前,圖片的大小都會被一般化。我們會希望我們的照片尺寸是 256 x 256 像素,DIGITS 等一下會自動裁切或縮放(這裡選擇縮放)我們的圖片。那些檔案名稱你要怎麼取根本沒差——重要的是它們是在什麼分類裡。 180 | 181 | > 問:「我可以再細分我的分類嗎?」 182 | 183 | 可以。詳閱 https://github.com/NVIDIA/DIGITS/blob/digits-4.0/docs/ImageFolderFormat.md 。 184 | 185 | 我們將使用這些在硬碟上的照片來建立一個**新的資料集**,而且是一個**分類用資料集**。 186 | 187 | ![建立一個資料集](images/create-new-dataset.png?raw=true "建立一個資料集") 188 | 189 | 我們將使用 DIGITS 的默認設定,然後將 **Training Images** 指向我們 [data/dolphins-and-seahorses](data/dolphins-and-seahorses) 的資料夾。DIGITS 將會以 `dolphin` 與 `seahorse` 這兩個分類來建立一個縮放好(256 x 256)的資料集,其中的 75% 用來訓練,另外的 25% 用來測試。 190 | 191 | 給你的資料集取個名字:`dolphins-and-seahorses`,然後點選 **Create**。 192 | 193 | ![新的影像辨識資料集](images/new-image-classification-dataset.png?raw=true "新的影像辨識資料集") 194 | 195 | 這會建立我們的資料集,在我的筆電上只用了 4 秒就跑完了。最後我在兩個類別裡共有 92 個訓練用圖片(49 個海豚和 43 個海馬)和 30 個驗證用圖片(16 個海豚和 43 海馬)。這是個十分小的資料集,不過對於我們的實驗和學習用途十分完美——訓練及驗證一個用這個資料集的網絡不會花我們一輩子的時間。 196 | 197 | 如果你想看看縮放之後的圖片,你可以**瀏覽資料庫**。 198 | 199 | ![Explore the db](images/explore-dataset.png?raw=true "Explore the db") 200 | 201 | ### 訓練:第一次嘗試,從頭開始訓練 202 | 203 | 回到 DIGITS 的主畫面,我們需要先建立一個新的**分類用模型**: 204 | 205 | ![建立分類用模型](images/create-classification-model.png?raw=true "建立分類用模型") 206 | 207 | 我們將從訓練一個使用我們 `dolphins-and-seahorses` 資料集的模型開始,我們將以 DIGITS 給的默認設定值來訓練它。這是我們的第一個網絡,我們選擇使用一個標準的網絡架構——「[AlexNet (pdf)](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf)」。[AlexNet 的設計](http://vision.stanford.edu/teaching/cs231b_spring1415/slides/alexnet_tugce_kyunghee.pdf) 在 2012 年贏得了一個大型的電腦視覺比賽——ImageNet。這個比賽要求將一百二十萬個圖像分類到一千多種不同的分類中。 208 | 209 | ![新的分類用模型 1](images/new-image-classification-model-attempt1.png?raw=true "新的分類用模型 1") 210 | 211 | Caffe 使用結構化的文字檔案來定義網絡架構。這些檔案使用的是 [Google 的 Protocol Buffers](https://developers.google.com/protocol-buffers/)。你可以閱讀 Caffe 使用的[整個架構](https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto)。 212 | 這不是我們主要要處理的部分,不過他們的存在值得我們注意,因為我們等會要修改他們。AlexNet 的 prototxt 檔案長這樣,例如:https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt 。 213 | 214 | 我們將會訓練我們的網絡 **30 個循環週期**。這表示網絡會使用我們的訓練圖片來學習,接著使用驗證圖片來測試他自己,然後根據結果來調整網絡的權重,然後重複這整個過程三十遍。當它每次完成一個循環之後我們會得到它的**準確度(_accuracy_)**(0% ~ 100%,越高越好)以及**損失(_loss_)**(所有錯誤的總和,值越低越好)。對於一個網路而言,最理想的狀況是有高準確度與最低的損失。 215 | 216 | 一開始,我們網路的準確度大致低於 50%。這十分合理,因為它一開始只是在用隨機的權重值來在兩個分類之間進行猜測。隨著訓練的時間增加,它的準確度可以達到 87.5%,且損失為 0.37。我的電腦用了不到六分鐘的時間就跑完這 30 個循環週期了。 217 | 218 | ![模型 嘗試 1](images/model-attempt1.png?raw=true "模型 嘗試 1") 219 | 220 | 我們可以上傳一張照片或一個圖片的 URL 來測試我們的模型。讓我們使用一些不在我們資料集的圖片來測試看看: 221 | 222 | ![模型 1 分類 1](images/model-attempt1-classify1.png?raw=true "模型 1 分類 1") 223 | 224 | ![模型 1 分類 2](images/model-attempt1-classify2.png?raw=true "模型 1 分類 2") 225 | 226 | 看起來還蠻不錯的,但是這一張的話 ...: 227 | 228 | ![模型 1 分類 3](images/model-attempt1-classify3.png?raw=true "模型 1 分類 3") 229 | 230 | 我們的網絡在這張圖上完全失敗了,而且將海馬混淆成了海豚,更糟糕的是,它十分有自信地認為這張圖是海豚。 231 | 232 | 事實上其實是我們的資料集太小了,沒辦法訓練一個很好的類神經網絡。我們很需要上萬甚至數十萬張照片來訓練,如果要這樣,我們還會需要十分強大的運算能力來處理這些照片。 233 | 234 | ### 訓練:第二次嘗試,微調 AlexNet 235 | 236 | #### 微調背後的原理 237 | 238 | > 譯者有話要說(Translator's Note):本段的內容較為複雜(很多專業術語),因此我的翻譯可能沒有很好。如果你願意,你可以看看原文,並希望你能順便幫忙改進翻譯,謝謝。 239 | 240 | 從頭開始設計一個類神經網絡、取得足夠的資料來訓練它(例如上百萬張照片)以及用好幾周的時間使用 GPU 來運算已經超出我們大多數人的能力範圍了。如果要使用較少的資料來訓練,我們會採用一個叫做「**遷移學習(_Transfer Learning_)**」的技術,也有人稱之為「**微調(_Fine Tuning_)**」。「微調」利用深層類神經網絡的架構及事先訓練好的網絡來達成一開始的物件偵測。 241 | 242 | 想像一下你一拿起望遠鏡要看很遠很遠的東西的時候,你會先將望遠鏡貼近你的眼睛,接著你看到的一切都是模糊的。隨著望遠鏡的焦距的調整,你會慢慢開始看見顏色、線條、形狀…… 慢慢地你就能看清楚一隻鳥的形狀。再稍微調整一下,你就能辨識這隻鳥的種類了。這就是使用一個類神經網絡的過程。 243 | 244 | 在一個多層網絡中,初始層(_initial layer_)提取一些特徵(例如:邊緣),接下來的層使用這些特徵來偵測形狀(例如輪子與眼睛),然後送到以在之前的層累積的特徵來偵測物件的最終分類層(例如一隻貓跟一隻狗)。一個網絡必須能夠從像素點開始掃描,到圓形、到眼睛、到朝著特定方向的兩個眼睛等等,直到最終能夠斷定這個照片內描繪的是一隻貓。 245 | 246 | 我們想做的是讓一個現有的、事先訓練好的網絡能夠專門來分類一些全新的影像分類,而不是讓它來分類當初用來訓練這個網絡的圖形。之所以這樣做是因為這種網絡已經知道如何「看見」圖形中的特徵,然後我們要重新訓練它來讓它能「看見」我們要他分類的特殊圖形。我們不需要從頭開始設定大多數的層——我們想要轉移這些已經學習好的層到我們的新分類任務。不像我們之前的訓練嘗試使用的是隨機的權重,我們這次要使用最終網絡中已有的權重來進行訓練。總而言之,我們將把最終的分類層丟掉,然後用**我們自己**的影像資料集來重新訓練它,將他微調到我們自己的影像分類。 247 | 248 | 如果要這樣做,我們需要一個與所需資料足夠相似的現有網絡,這樣它學習到的權重對我們來說才會有用處。幸運的是我們接下來將使用的網絡是曾使用上百萬個來自 [ImageNet](http://image-net.org/) 大自然的照片來進行訓練的網絡,因此它對非常多種不同的分類任務都十分的有用處。 249 | 250 | 這項技術常被用來做有趣的事情,例如從自醫學圖像中掃描是否有眼部疾病、識別從海上採集的浮游生物顯微圖像,到分類 Flickr 網站圖片的藝術風格。 251 | 252 | 跟所有的機器學習一樣,如果你想做到完美,你需要了解你的資料以及網絡架構——你必須注意這些資料是否會造成過度學習(_overfitting_)、你可能需要修復其中幾層,或是加入新的幾層,諸如此類。總之,我的經驗是它在大多數的時候是可行的,你值得試試看,看你能用我們的方法做得如何。 253 | 254 | #### 上傳已事先訓練好的網絡 255 | 256 | 在我們第一次的嘗試中,我們使用了 AlexNet 的架構,但是在該網絡的層中我們以隨機的權重來開始訓練。我們現在希望能夠下載並使用一個已經使用龐大的資料集來訓練過的 AlexNet 版本。 257 | 258 | 令人感激的是我們完全可以這樣做。一個 AlexNet 快照(_snapshot_)可以在這裡下載:https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet 。 259 | 我們需要 `.caffemodel` 檔案,它裡面包含了已經訓練過的權重。我們可以在此下載它:http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel 。 260 | 261 | 當你在下載他們的時候,我們再順便多下載一個吧。在 2014 年,Google 使用了一個 22 層的類神經網絡 [GoogLeNet](https://research.google.com/pubs/pub43022.html) (代號為「Inception」)贏了同一個 ImageNet 比賽: 262 | GoogLeNet 也有個快照可以下載,在這裡:https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenet 。跟上次一樣,我們會需要含有已訓練過權重的 `.caffemodel` 檔案,你可以在這裡下載它:http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel 。 263 | 264 | 有了這些 `.caffemodel` 檔案,我們就可以把他們上傳到 DIGITS 裡了。在 DIGITS 的首頁選擇「**Pretrained Models**」然後選擇 「**Upload Pretrained Model**」: 265 | 266 | ![載入事先訓練好的模型](images/load-pretrained-model.png?raw=true "載入事先訓練好的模型") 267 | 268 | 這兩個模型我們都使用 DIGITS 提供的預設設定。我們只需要提供 `Weights (**.caffemodel)` ,即權重值檔案 `.caffemodel` 以及 `Model Definition (original.prototxt)` 模型定義檔案 `original.prototxt`。點一下對應的按鈕並選擇你的檔案就可以上傳。 269 | 270 | GoogLeNet 的模型定義檔案我們使用 https://github.com/BVLC/caffe/blob/master/models/bvlc_googlenet/train_val.prototxt ,AlexNet 的我們使用 https://github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt 。我們不會使用到分類標籤(_classification labels_),所以我們將跳過 `labels.txt`。 271 | 272 | ![上傳事先訓練好的模型](images/upload-pretrained-model.png?raw=true "上傳事先訓練好的模型") 273 | 274 | 記得兩個網絡(AlexNet 與 GoogLeNet)都要上傳,兩個網絡我們下面都會用到。 275 | 276 | > 問:「有其他可以拿來微調的網絡嗎?」 277 | 278 | [Caffe Model Zoo](http://caffe.berkeleyvision.org/model_zoo.html) 還有蠻多可以用的已訓練好的網絡,詳閱 https://github.com/BVLC/caffe/wiki/Model-Zoo 。 279 | 280 | #### 針對海豚與海馬來微調 AlexNet 281 | 282 | 用一個已訓練好的 Caffe 模型來訓練一個網絡還蠻像是從頭開始訓練的,只不過我們需要做一些細微的調整。首先,我們將調整**基礎學習速率**(_**Base Learning Rate**_),因為我們不需要很大的變動(我們在微調),因此我們將把它從 0.01 改為 0.001。接下來選取下面的「**Pretrained Network**(**事先訓練好的網絡**)」,然後選擇 **Customize**(**自定義**)。 283 | 284 | ![新的圖像分類用模型](images/new-image-classification-model-attempt2.png?raw=true "新的圖像分類用模型") 285 | 286 | 在事先訓練好的模型的 prototext 定義中,我們需要將所有參考重命名到最終的**全連結層(_Fully Connected Layer_)**,全連結層負責最終分類。我們這樣做是因為我們希望模型自我們自己的資料集中重新學習新的分類,而不是使用它原本的訓練資料——我們要把它目前的最終層丟掉。我們必須將最終全連結層的名字「fc8」改為別的名字,就改成「fc9」好了。最後,我們需要把類別數量從 `1000` 改為 `2`,也就是將 `num_output` 改為 `2`。 287 | 288 | 這是我們所需要作出的更動: 289 | 290 | ```diff 291 | @@ -332,8 +332,8 @@ 292 | } 293 | layer { 294 | - name: "fc8" 295 | + name: "fc9" 296 | type: "InnerProduct" 297 | bottom: "fc7" 298 | - top: "fc8" 299 | + top: "fc9" 300 | param { 301 | lr_mult: 1 302 | @@ -345,5 +345,5 @@ 303 | } 304 | inner_product_param { 305 | - num_output: 1000 306 | + num_output: 2 307 | weight_filler { 308 | type: "gaussian" 309 | @@ -359,5 +359,5 @@ 310 | name: "accuracy" 311 | type: "Accuracy" 312 | - bottom: "fc8" 313 | + bottom: "fc9" 314 | bottom: "label" 315 | top: "accuracy" 316 | @@ -367,5 +367,5 @@ 317 | name: "loss" 318 | type: "SoftmaxWithLoss" 319 | - bottom: "fc8" 320 | + bottom: "fc9" 321 | bottom: "label" 322 | top: "loss" 323 | @@ -375,5 +375,5 @@ 324 | name: "softmax" 325 | type: "Softmax" 326 | - bottom: "fc8" 327 | + bottom: "fc9" 328 | top: "softmax" 329 | include { stage: "deploy" } 330 | ``` 331 | 332 | 這裡有一份我實際在使用的修改過後的檔案:[src/alexnet-customized.prototxt](src/alexnet-customized.prototxt)。 333 | 334 | 這次我們的準確度從 60% 上下然後立刻爬升到 87.5%,接著再到 96% 然後一路上升到 100%,損失也穩定地下降。五分鐘之後我們的結果是 100% 的準確度與 0.0009 的損失。 335 | 336 | ![模型訓練嘗試 2](images/model-attempt2.png?raw=true "模型訓練嘗試 2") 337 | 338 | 測試我們前一個網絡判斷錯誤的同一張照片,我們可以看到一個極大的差距:這次的結果是 100% 海馬。 339 | 340 | ![模型 2 分類 1](images/model-attempt2-classify1.png?raw=true "模型 2 分類 1") 341 | 342 | 就算是一個小孩畫的海馬都可以: 343 | 344 | ![模型 2 分類 2](images/model-attempt2-classify2.png?raw=true "模型 2 分類 2") 345 | 346 | 海豚的結果也一樣: 347 | 348 | ![模型 2 分類 3](images/model-attempt2-classify3.png?raw=true "模型 2 分類 3") 349 | 350 | 甚至你覺得可能很難判斷的照片,像是這張照片裡面有很多隻海豚靠在一起,且他們的身體幾乎都在水下,我們的網絡還是能給出正確的答案: 351 | 352 | ![模型 2 分類 4](images/model-attempt2-classify4.png?raw=true "模型 2 分類 4") 353 | 354 | 355 | ### 訓練:第三次嘗試,微調 GoogLeNet 356 | 357 | 像是前面被我們拿來微調的的 AlexNet 模型,我們一樣可以用在 GoogLeNet 上。要修改 GoogLeNet 有點棘手,因為你需要重新定義三個全連結層,上次我們只重新定義了一個。 358 | 359 | 我們要再一次建立一個新的**分類用模型**(_**Classification Model**_)以微調 GoogLeNet 至我們想要的狀態。 360 | 361 | ![新的分類用模型](images/new-image-classification-model-attempt3.png?raw=true "新的分類用模型") 362 | 363 | 我們將重新命名所有到這三個全連結辨識層的參考:`loss1/classifier`、`loss2/classifier` 和 `loss3/classifier`。接著我們要重新設定類別的數量(`num_output: 2`)。以下是我們需要更動的地方以修改上述設定: 364 | 365 | ```diff 366 | @@ -917,10 +917,10 @@ 367 | exclude { stage: "deploy" } 368 | } 369 | layer { 370 | - name: "loss1/classifier" 371 | + name: "loss1a/classifier" 372 | type: "InnerProduct" 373 | bottom: "loss1/fc" 374 | - top: "loss1/classifier" 375 | + top: "loss1a/classifier" 376 | param { 377 | lr_mult: 1 378 | decay_mult: 1 379 | @@ -930,7 +930,7 @@ 380 | decay_mult: 0 381 | } 382 | inner_product_param { 383 | - num_output: 1000 384 | + num_output: 2 385 | weight_filler { 386 | type: "xavier" 387 | std: 0.0009765625 388 | @@ -945,7 +945,7 @@ 389 | layer { 390 | name: "loss1/loss" 391 | type: "SoftmaxWithLoss" 392 | - bottom: "loss1/classifier" 393 | + bottom: "loss1a/classifier" 394 | bottom: "label" 395 | top: "loss1/loss" 396 | loss_weight: 0.3 397 | @@ -954,7 +954,7 @@ 398 | layer { 399 | name: "loss1/top-1" 400 | type: "Accuracy" 401 | - bottom: "loss1/classifier" 402 | + bottom: "loss1a/classifier" 403 | bottom: "label" 404 | top: "loss1/accuracy" 405 | include { stage: "val" } 406 | @@ -962,7 +962,7 @@ 407 | layer { 408 | name: "loss1/top-5" 409 | type: "Accuracy" 410 | - bottom: "loss1/classifier" 411 | + bottom: "loss1a/classifier" 412 | bottom: "label" 413 | top: "loss1/accuracy-top5" 414 | include { stage: "val" } 415 | @@ -1705,10 +1705,10 @@ 416 | exclude { stage: "deploy" } 417 | } 418 | layer { 419 | - name: "loss2/classifier" 420 | + name: "loss2a/classifier" 421 | type: "InnerProduct" 422 | bottom: "loss2/fc" 423 | - top: "loss2/classifier" 424 | + top: "loss2a/classifier" 425 | param { 426 | lr_mult: 1 427 | decay_mult: 1 428 | @@ -1718,7 +1718,7 @@ 429 | decay_mult: 0 430 | } 431 | inner_product_param { 432 | - num_output: 1000 433 | + num_output: 2 434 | weight_filler { 435 | type: "xavier" 436 | std: 0.0009765625 437 | @@ -1733,7 +1733,7 @@ 438 | layer { 439 | name: "loss2/loss" 440 | type: "SoftmaxWithLoss" 441 | - bottom: "loss2/classifier" 442 | + bottom: "loss2a/classifier" 443 | bottom: "label" 444 | top: "loss2/loss" 445 | loss_weight: 0.3 446 | @@ -1742,7 +1742,7 @@ 447 | layer { 448 | name: "loss2/top-1" 449 | type: "Accuracy" 450 | - bottom: "loss2/classifier" 451 | + bottom: "loss2a/classifier" 452 | bottom: "label" 453 | top: "loss2/accuracy" 454 | include { stage: "val" } 455 | @@ -1750,7 +1750,7 @@ 456 | layer { 457 | name: "loss2/top-5" 458 | type: "Accuracy" 459 | - bottom: "loss2/classifier" 460 | + bottom: "loss2a/classifier" 461 | bottom: "label" 462 | top: "loss2/accuracy-top5" 463 | include { stage: "val" } 464 | @@ -2435,10 +2435,10 @@ 465 | } 466 | } 467 | layer { 468 | - name: "loss3/classifier" 469 | + name: "loss3a/classifier" 470 | type: "InnerProduct" 471 | bottom: "pool5/7x7_s1" 472 | - top: "loss3/classifier" 473 | + top: "loss3a/classifier" 474 | param { 475 | lr_mult: 1 476 | decay_mult: 1 477 | @@ -2448,7 +2448,7 @@ 478 | decay_mult: 0 479 | } 480 | inner_product_param { 481 | - num_output: 1000 482 | + num_output: 2 483 | weight_filler { 484 | type: "xavier" 485 | } 486 | @@ -2461,7 +2461,7 @@ 487 | layer { 488 | name: "loss3/loss" 489 | type: "SoftmaxWithLoss" 490 | - bottom: "loss3/classifier" 491 | + bottom: "loss3a/classifier" 492 | bottom: "label" 493 | top: "loss" 494 | loss_weight: 1 495 | @@ -2470,7 +2470,7 @@ 496 | layer { 497 | name: "loss3/top-1" 498 | type: "Accuracy" 499 | - bottom: "loss3/classifier" 500 | + bottom: "loss3a/classifier" 501 | bottom: "label" 502 | top: "accuracy" 503 | include { stage: "val" } 504 | @@ -2478,7 +2478,7 @@ 505 | layer { 506 | name: "loss3/top-5" 507 | type: "Accuracy" 508 | - bottom: "loss3/classifier" 509 | + bottom: "loss3a/classifier" 510 | bottom: "label" 511 | top: "accuracy-top5" 512 | include { stage: "val" } 513 | @@ -2489,7 +2489,7 @@ 514 | layer { 515 | name: "softmax" 516 | type: "Softmax" 517 | - bottom: "loss3/classifier" 518 | + bottom: "loss3a/classifier" 519 | top: "softmax" 520 | include { stage: "deploy" } 521 | } 522 | ``` 523 | 524 | 我已經將完整的檔案放在了 [src/googlenet-customized.prototxt](src/googlenet-customized.prototxt)。 525 | 526 | > 問:「那對於這些網絡的 prototext 定義修改呢?我們已經修改了全連接層的名字還有類別的數量,還有什麼是我們可以或是應該要修改的東西,且是在什麼情況下?」 527 | 528 | 很棒的問題,這也是我很想知道的事情。舉例來說,我知道我們可以[「修復」特定的「層」](https://github.com/BVLC/caffe/wiki/Fine-Tuning-or-Training-Certain-Layers-Exclusively),這樣權重值就不會變動。做別的事情需要理解這些層背後的原理,這已經超出本教學的範圍,也已經超出本教學作者的知識範圍! 529 | 530 | 就像我們對 AlexNet 所做的微調,我們也降低了 10% 的學習速率(_learning rate_),即從 `0.01` 降低到 `0.001`。 531 | 532 | > 問:「在微調時,還有哪些有意義的其他的更動?例如不同的循環週期數(_epochs_)怎麼樣?批尺寸(_batch sizes_)、求解方法(Adam、AdaDelta、AdaGrad 之類的)呢?學習速率(_learning rates_)、策略(Exponential Decay、Inverse Decay 和 Sigmoid Decay 等等)、步長和 gamma 值呢?」 533 | 534 | 很好的問題,而且也是個我很好奇的問題。我對這些東西也只有很模糊的理解,如果你知道訓練時要如何調整這些數值,我們的設定也應該可以做出一些改進。這東西需要更好的說明文件。 535 | 536 | 因為 GoogLeNet 的結構比 AlexNet 複雜得多,微調它要花上更多時間。我用了十分鐘用我們的資料集重新在我的筆電上訓練它,達到了 100% 的準確度以及 0.0070 的損失。 537 | 538 | ![模型 第三次訓練嘗試](images/model-attempt3.png?raw=true "模型 第三次訓練嘗試 3 辨識 3") 539 | 540 | 跟我們看到微調後 AlexNet 的表現一樣,我們修改過的 GoogLeNet 表現的也十分出色——它是我們目前訓練出最好的模型。 541 | 542 | ![模型 第三次訓練嘗試 3 辨識 1](images/model-attempt3-classify1.png?raw=true "模型 第三次訓練嘗試 3 辨識 1") 543 | 544 | ![模型 第三次訓練嘗試 3 辨識 2](images/model-attempt3-classify2.png?raw=true "模型 第三次訓練嘗試 3 辨識 2") 545 | 546 | ![模型 第三次訓練嘗試 3 辨識 3](images/model-attempt3-classify3.png?raw=true "模型 第三次訓練嘗試 3 辨識 3") 547 | 548 | ## 使用我們的模型 549 | 550 | 我們已經訓練並測試好了我們的網絡,是時候下載並實際使用它了。每個我們在 DIGITS 內訓練的模型都有個 **Download Model**(**下載模型**) 的按鈕,也可以用來選擇不同的訓練時快照——例如 `Epoch #30`(`循環週期 #30`): 551 | 552 | ![訓練完成的模型](images/trained-models.png?raw=true “訓練完成的模型”) 553 | 554 | 按下 **Download Model** 將會下載一個 `tar.gz` 壓縮檔,裡面有這些檔案: 555 | 556 | ``` 557 | deploy.prototxt 558 | mean.binaryproto 559 | solver.prototxt 560 | info.json 561 | original.prototxt 562 | labels.txt 563 | snapshot_iter_90.caffemodel 564 | train_val.prototxt 565 | ``` 566 | 567 | 這裡有個對於如何使用我們剛訓練好的模型的一個[不錯的說明](https://github.com/BVLC/caffe/wiki/Using-a-Trained-Network:-Deploy),裡面談到了: 568 | 569 | > 一個網絡是以其設計(.prototxt)及其權重(.caffemodel)來定義的。 570 | > 當一個網絡在訓練時,該網絡目前的權重狀態存在一個 .caffemodel 檔案中。 571 | > 當有了這兩個檔案,我們就可以從訓練及測試階段進入成品階段(_production phase_)了。 572 | > 573 | > 在它目前的狀態下,這個網絡的設計還沒有為部署準備好。在我們將我們的網絡釋出為產品前,我們常需要用以下的方法來調整它: 574 | > 575 | > 1. 將用來訓練的資料層刪除,因為在分類時我們將不會再為我們的資料提供標籤。 576 | > 2. 刪除任何依賴於資料標籤的層。 577 | > 3. 將網絡設定為可接受資料。 578 | > 4. 確認網絡可以輸出結果。 579 | 580 | DIGITS 已經幫我們把這些問題都解決了,也幫我們分離了不同的 `prototxt` 檔案版本。當我們在使用這個網絡的時候我們將會用到以下檔案: 581 | 582 | * `deploy.prototxt` —— 網絡的定義檔案,準備好接受影像輸入資料 583 | * `mean.binaryproto` —— 我們的模型會需要我們為每個它要處理的影像減去影像平均值(_image mean_),且這是平均影像資料(_the mean image_)。 584 | * `labels.txt` —— 一個放了我們所有標籤的列表(`dolphin` 與 `seahorse`),如果我們想看到網絡輸出的是這些標籤而不是類別編號時,這個派的上用場。 585 | * `snapshot_iter_90.caffemodel` —— 這是我們網絡訓練好的權重 586 | 587 | 我們可以用不少方式以這些檔案來分類新的影像。例如,在我們的 `CAFFE_ROOT` 下,我們可以使用 `build/examples/cpp_classification/classification.bin` 來分類一個影像: 588 | 589 | ```bash 590 | $ cd $CAFFE_ROOT/build/examples/cpp_classification 591 | $ ./classification.bin deploy.prototxt snapshot_iter_90.caffemodel mean.binaryproto labels.txt dolphin1.jpg 592 | ``` 593 | 594 | 這會噴出一堆 debug 資訊,接下來是分別對兩個類別的預測結果: 595 | 596 | ``` 597 | 0.9997 - “dolphin” 598 | 0.0003 - “seahorse” 599 | ``` 600 | 601 | 你可以在 [Caffe 範例](https://github.com/BVLC/caffe/tree/master/examples)中閱讀這個東西的[完整 C++ 原始碼](https://github.com/BVLC/caffe/tree/master/examples/cpp_classification)。 602 | 603 | 對於 Python 應用程式來說,DIGITS 也有提供一個[不錯的範例](https://github.com/NVIDIA/DIGITS/tree/master/examples/classification)。Caffe 範例中也有一個[十分詳細的 Python 版教學](https://github.com/BVLC/caffe/blob/master/examples/00-classification.ipynb)。 604 | 605 | ### Python 示例 606 | 607 | 我們來寫一個用圖像分類程式,使用我們微調過的 GoogLeNet 模型來分類我們現有的未經訓練的圖片,它們在 [data/untrained-samples](data/untrained-samples) 裡。我已經把上面的例子都組合了起來,雖然 `caffe` [Python module 的原始碼](https://github.com/BVLC/caffe/tree/master/python) 也已經有了,但是你應該還是會比較喜歡我接下來要講的範例。 608 | 609 | 接下來我要講的內容都在 [src/classify-samples.py](src/classify-samples.py) 裡,讓我們開始吧! 610 | 611 | 首先,我們會需要一個叫 [NumPy](http://www.numpy.org/) 的模組(_module_)。我們等下會用它來操作 Caffe 大量使用的 [`ndarray`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html)。如果你還沒使用過他們(我也沒有),你可以先閱讀這篇[快速入門教學](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html)。 612 | 613 | 接下來,我們會需要從 `CAFFE_ROOT` 載入 `caffe` 模組。如果它還沒有被加入到你的 Python 環境裡,你可以手動加入以強制載入它,我們也會順便載入 Caffe 的 protobuf 模組。 614 | 615 | ```python 616 | import numpy as np 617 | 618 | caffe_root = '/path/to/your/caffe_root' 619 | sys.path.insert(0, os.path.join(caffe_root, 'python')) 620 | import caffe 621 | from caffe.proto import caffe_pb2 622 | ``` 623 | 624 | 接下來我們需要告訴 Caffe 要用 [CPU 還是 GPU](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L50-L52)。 625 | 對於我們的實驗來說,CPU 就夠了: 626 | 627 | ```python 628 | caffe.set_mode_cpu() 629 | ``` 630 | 631 | 現在我們可以使用 `caffe` 來載入我們訓練的網絡,我們會需要一些我們剛從 DIGITS 下載好的檔案: 632 | 633 | * `deploy.prototxt` —— 我們的「網絡檔案」,即網絡的描述。 634 | * `snapshot_iter_90.caffemodel` —— 我們訓練好的「權重」資料 635 | 636 | 我們很顯然地需要提供完整的路徑(_full path_),我假設我的這些檔案放在一個叫做 `model/` 的資料夾: 637 | 638 | ```python 639 | model_dir = 'model' 640 | deploy_file = os.path.join(model_dir, 'deploy.prototxt') 641 | weights_file = os.path.join(model_dir, 'snapshot_iter_90.caffemodel') 642 | net = caffe.Net(deploy_file, caffe.TEST, weights=weights_file) 643 | ``` 644 | `caffe.Net()` 的[構造函數(_constructor_)](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L91-L117)需要一個網絡檔案、一個階段描述(`caffe.TEST` 或 `caffe.TRAIN`)與一個(可選的)權重檔案名稱。當我們提供一個權重檔案時,`Net` 將會自動幫我們載入它。`Net` 有著不少你可以用的[方法與屬性](https://github.com/BVLC/caffe/blob/master/python/caffe/pycaffe.py)。 645 | 646 | **注:** 這個構造函數也有一個[已棄用的版本](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/_caffe.cpp#L119-L134),它看起來常常在網絡上的範例程式碼中出現。如果你遇到了它,它看起來會像是這樣: 647 | 648 | ```python 649 | net = caffe.Net(str(deploy_file), str(model_file), caffe.TEST) 650 | ``` 651 | 652 | 我們之後會將各式各樣大小的照片丟到我們的網絡中進行測試。因此,我們將把這些照片**轉換**成一個我們的網絡可以用的形狀(colour、256x256)。Caffe 提供了一個 [`Transformer` class](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L98) 專門用來處理這種情況。我們將會使用它來建立一個適合我們的影像及網絡的轉換器: 653 | 654 | ```python 655 | transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) 656 | # set_transpose: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L187 657 | transformer.set_transpose('data', (2, 0, 1)) 658 | # set_raw_scale: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L221 659 | transformer.set_raw_scale('data', 255) 660 | # set_channel_swap: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L203 661 | transformer.set_channel_swap('data', (2, 1, 0)) 662 | ``` 663 | 664 | 我們也可以使用 DIGITS 給了我們的 `mean.binaryproto` 檔案來設定我們的轉換器: 665 | 666 | ```python 667 | # This code for setting the mean from https://github.com/NVIDIA/DIGITS/tree/master/examples/classification 668 | mean_file = os.path.join(model_dir, 'mean.binaryproto') 669 | with open(mean_file, 'rb') as infile: 670 | blob = caffe_pb2.BlobProto() 671 | blob.MergeFromString(infile.read()) 672 | if blob.HasField('shape'): 673 | blob_dims = blob.shape 674 | assert len(blob_dims) == 4, 'Shape should have 4 dimensions - shape is %s' % blob.shape 675 | elif blob.HasField('num') and blob.HasField('channels') and \ 676 | blob.HasField('height') and blob.HasField('width'): 677 | blob_dims = (blob.num, blob.channels, blob.height, blob.width) 678 | else: 679 | raise ValueError('blob does not provide shape or 4d dimensions') 680 | pixel = np.reshape(blob.data, blob_dims[1:]).mean(1).mean(1) 681 | transformer.set_mean('data', pixel) 682 | ``` 683 | 684 | 如果我們有很多標籤,我們也可以選擇讀取我們的標籤檔案以用作稍後輸出機率的標籤(如:0=dolphin,1=seahorse): 685 | 686 | ```python 687 | labels_file = os.path.join(model_dir, 'labels.txt') 688 | labels = np.loadtxt(labels_file, str, delimiter='\n') 689 | ``` 690 | 691 | 現在我們已經準備好來辨識一個影像了。我們要使用 [`caffe.io.load_image()`](https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L279) 來讀取我們的影像檔案,然後再使用我們的轉換器來重塑它,最後將它設定為我們網絡的資料層: 692 | 693 | ```python 694 | # Load the image from disk using caffe's built-in I/O module 695 | image = caffe.io.load_image(fullpath) 696 | # Preprocess the image into the proper format for feeding into the model 697 | net.blobs['data'].data[...] = transformer.preprocess('data', image) 698 | ``` 699 | 700 | > 問:「我要怎麼測試來自相機或視訊流(幀)的影像而不是使用檔案來測試?」 701 | 702 | 很棒的問題,以下是個供你開始的範例: 703 | 704 | ```python 705 | import cv2 706 | ... 707 | # Get the shape of our input data layer, so we can resize the image 708 | input_shape = net.blobs['data'].data.shape 709 | ... 710 | webCamCap = cv2.VideoCapture(0) # could also be a URL, filename 711 | if webCamCap.isOpened(): 712 | rval, frame = webCamCap.read() 713 | else: 714 | rval = False 715 | 716 | while rval: 717 | rval, frame = webCamCap.read() 718 | net.blobs['data'].data[...] = transformer.preprocess('data', frame) 719 | ... 720 | 721 | webCamCap.release() 722 | ``` 723 | 724 | 回到我們的問題,我們接下來需要將我們的影像資料跑一遍我們的網絡然後再讀取我們網絡最終的 `'softmax'` 層返回的機率值,這個機率會依照我們的標籤分類來排序: 725 | 726 | ```python 727 | # Run the image's pixel data through the network 728 | out = net.forward() 729 | # Extract the probabilities of our two categories from the final layer 730 | softmax_layer = out['softmax'] 731 | # Here we're converting to Python types from ndarray floats 732 | dolphin_prob = softmax_layer.item(0) 733 | seahorse_prob = softmax_layer.item(1) 734 | 735 | # Print the results. I'm using labels just to show how it's done 736 | label = labels[0] if dolphin_prob > seahorse_prob else labels[1] 737 | filename = os.path.basename(fullpath) 738 | print '%s is a %s dolphin=%.3f%% seahorse=%.3f%%' % (filename, label, dolphin_prob*100, seahorse_prob*100) 739 | ``` 740 | 741 | 使用我們微調的 GoogLeNet 以這整個程式(見 [src/classify-samples.py](src/classify-samples.py))來測試我們的 [data/untrained-samples](data/untrained-samples) 影像,我得到了這些輸出: 742 | 743 | ``` 744 | [...truncated caffe network output...] 745 | dolphin1.jpg is a dolphin dolphin=99.968% seahorse=0.032% 746 | dolphin2.jpg is a dolphin dolphin=99.997% seahorse=0.003% 747 | dolphin3.jpg is a dolphin dolphin=99.943% seahorse=0.057% 748 | seahorse1.jpg is a seahorse dolphin=0.365% seahorse=99.635% 749 | seahorse2.jpg is a seahorse dolphin=0.000% seahorse=100.000% 750 | seahorse3.jpg is a seahorse dolphin=0.014% seahorse=99.986% 751 | ``` 752 | 753 | 我還在試著學習所有使用程式碼來處理模型的最佳實踐。我很希望我能告訴你們更多更好的程式碼範例、API 與現有的模組等等。 754 | 老實說,我找到的大多數程式碼範例都很簡潔,而且文件都寫的很糟糕——Caffe 的文件有很多問題,而且有著很多的假設。 755 | 756 | 在我看來,應該有人能夠以 Caffe 介面為基礎來建立更高級別的工具與基本的工作流程,也就是我們以上所做的事情。如果在高級語言中有更多我能夠跟你指出它有「正確地使用我們的模型」的更簡單模組,那一定會很棒;應該有人能夠做到這一點,並且讓*使用* Caffe 模型跟使用 DIGITS 來*訓練*這些模型一樣簡單。舉例來說,我很希望我能夠用 node.js 來操作這些東西。最理想的情況是有一天沒有人會需要知道這麼多有關於模型與 Caffe 的運作模式。[DeepDetect](https://deepdetect.com/) 在這一點看起來十分的有趣,不過我還沒使用過它。而且我認為還有很多我不知道的工具。 757 | 758 | ## 結果 759 | 760 | 在最初我們說了我們的目標是寫一個能夠使用一個類神經網絡來正確分類 [data/untrained-samples](data/untrained-samples) 中的所有照片的程式。這些是在上述過程中從來沒用來訓練或是測試過的海豚或海馬的圖片: 761 | 762 | ### 未訓練的海豚影像 763 | 764 | ![海豚 1](data/untrained-samples/dolphin1.jpg?raw=true "海豚 1") 765 | ![海豚 2](data/untrained-samples/dolphin2.jpg?raw=true "海豚 2") 766 | ![海豚 3](data/untrained-samples/dolphin3.jpg?raw=true "海豚 3") 767 | 768 | ### 未訓練的海馬影像 769 | 770 | ![海馬 1](data/untrained-samples/seahorse1.jpg?raw=true "海馬 1") 771 | ![海馬 2](data/untrained-samples/seahorse2.jpg?raw=true "海馬 2") 772 | ![海馬 3](data/untrained-samples/seahorse3.jpg?raw=true "海馬 3") 773 | 774 | 讓我們看看我們的三個訓練嘗試分別做得怎麼樣: 775 | 776 | ### 模型第一次訓練嘗試:從頭開始訓練的 AlexNet(第三名) 777 | 778 | | 照片 | 海豚 | 海馬 | 結果 | 779 | |-------|---------|----------|--------| 780 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 71.11% | 28.89% | :expressionless: | 781 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.2% | 0.8% | :sunglasses: | 782 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 63.3% | 36.7% | :confused: | 783 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 95.04% | 4.96% | :disappointed: | 784 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 56.64% | 43.36 | :confused: | 785 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 7.06% | 92.94% | :grin: | 786 | 787 | ### 模型第二次訓練嘗試: 微調後的 AlexNet(第二名) 788 | 789 | | 照片 | 海豚 | 海馬 | 結果 | 790 | |-------|---------|----------|--------| 791 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.1% | 0.09% | :sunglasses: | 792 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 99.5% | 0.05% | :sunglasses: | 793 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 91.48% | 8.52% | :grin: | 794 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0% | 100% | :sunglasses: | 795 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 796 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0% | 100% | :sunglasses: | 797 | 798 | ### 模型第三次訓練嘗試: 微調後的 GoogLeNet(第一名) 799 | 800 | | 照片 | 海豚 | 海馬 | 結果 | 801 | |-------|---------|----------|--------| 802 | |[dolphin1.jpg](data/untrained-samples/dolphin1.jpg)| 99.86% | 0.14% | :sunglasses: | 803 | |[dolphin2.jpg](data/untrained-samples/dolphin2.jpg)| 100% | 0% | :sunglasses: | 804 | |[dolphin3.jpg](data/untrained-samples/dolphin3.jpg)| 100% | 0% | :sunglasses: | 805 | |[seahorse1.jpg](data/untrained-samples/seahorse1.jpg)| 0.5% | 99.5% | :sunglasses: | 806 | |[seahorse2.jpg](data/untrained-samples/seahorse2.jpg)| 0% | 100% | :sunglasses: | 807 | |[seahorse3.jpg](data/untrained-samples/seahorse3.jpg)| 0.02% | 99.98% | :sunglasses: | 808 | 809 | ## 結論 810 | 811 | 我們的模型跑起來真的十分令人驚訝,微調一個事先訓練好的網絡之後的成效也是。很明顯的,我們使用海豚及海馬作為例子是故意設計好的,且我們的資料集也太有限了——如果我們希望我們的網絡變得很強大,我們真的會需要更多更好的資料。不過既然我們的目的是玩玩看類神經網絡的工具及工作流程,這個結果還是十分理想的,尤其是它不需要昂貴的設備或大量的時間。 812 | 813 | 我希望以上所有的經驗能讓你拋去所有剛踏進這個領域時產生的壓倒性恐懼。當你看過機器學習及類神經網絡實際運作的小例子之後,你應該能更容易地確定你是否值得在這個領域投入時間來學習它們背後的理論。現在你已經有了一個設定好的環境及一個可行的方法,你可以嘗試做做看其他類型的分類。你也可能會想要看看你還能用 Caffe 和 Digits 做什麼事情,例如在一個影像內尋找物件或是執行分割。 814 | 815 | 「Have fun with machine learning!」 816 | -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0001.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0002.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0003.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0004.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0005.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0006.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0007.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0008.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0009.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0010.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0011.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0012.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0013.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0014.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0015.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0016.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0017.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0017.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0018.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0019.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0019.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0020.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0021.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0022.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0023.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0024.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0025.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0025.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0026.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0027.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0028.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0029.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0030.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0031.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0031.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0032.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0032.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0033.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0033.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0034.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0034.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0035.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0035.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0036.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0036.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0037.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0037.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0038.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0038.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0039.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0039.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0040.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0040.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0041.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0041.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0042.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0042.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0043.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0043.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0044.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0044.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0045.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0045.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0046.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0046.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0047.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0047.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0048.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0049.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0049.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0050.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0051.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0051.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0052.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0052.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0053.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0053.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0054.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0054.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0055.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0055.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0056.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0056.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0057.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0057.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0058.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0058.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0059.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0059.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0060.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0060.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0061.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0061.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0062.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0062.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0063.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0063.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0064.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0064.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/dolphin/image_0065.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/dolphin/image_0065.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0001.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0002.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0003.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0004.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0005.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0006.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0007.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0008.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0009.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0010.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0011.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0012.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0013.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0014.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0015.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0016.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0017.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0017.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0018.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0019.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0019.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0020.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0021.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0022.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0023.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0024.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0025.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0025.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0026.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0027.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0028.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0029.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0030.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0031.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0031.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0032.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0032.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0033.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0033.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0034.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0034.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0035.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0035.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0036.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0036.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0037.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0037.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0038.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0038.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0039.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0039.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0040.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0040.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0041.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0041.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0042.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0042.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0043.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0043.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0044.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0044.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0045.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0045.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0046.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0046.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0047.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0047.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0048.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0049.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0049.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0050.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0051.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0051.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0052.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0052.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0053.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0053.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0054.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0054.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0055.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0055.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0056.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0056.jpg -------------------------------------------------------------------------------- /data/dolphins-and-seahorses/seahorse/image_0057.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/dolphins-and-seahorses/seahorse/image_0057.jpg -------------------------------------------------------------------------------- /data/untrained-samples/dolphin1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/dolphin1.jpg -------------------------------------------------------------------------------- /data/untrained-samples/dolphin2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/dolphin2.jpg -------------------------------------------------------------------------------- /data/untrained-samples/dolphin3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/dolphin3.jpg -------------------------------------------------------------------------------- /data/untrained-samples/list.txt: -------------------------------------------------------------------------------- 1 | dolphin1.jpg 2 | dolphin2.jpg 3 | dolphin3.jpg 4 | seahorse1.jpg 5 | seahorse2.jpg 6 | seahorse3.jpg 7 | -------------------------------------------------------------------------------- /data/untrained-samples/seahorse1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/seahorse1.jpg -------------------------------------------------------------------------------- /data/untrained-samples/seahorse2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/seahorse2.jpg -------------------------------------------------------------------------------- /data/untrained-samples/seahorse3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/data/untrained-samples/seahorse3.jpg -------------------------------------------------------------------------------- /images/create-classification-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/create-classification-model.png -------------------------------------------------------------------------------- /images/create-new-dataset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/create-new-dataset.png -------------------------------------------------------------------------------- /images/explore-dataset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/explore-dataset.png -------------------------------------------------------------------------------- /images/load-pretrained-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/load-pretrained-model.png -------------------------------------------------------------------------------- /images/model-attempt1-classify1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt1-classify1.png -------------------------------------------------------------------------------- /images/model-attempt1-classify2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt1-classify2.png -------------------------------------------------------------------------------- /images/model-attempt1-classify3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt1-classify3.png -------------------------------------------------------------------------------- /images/model-attempt1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt1.png -------------------------------------------------------------------------------- /images/model-attempt2-classify1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt2-classify1.png -------------------------------------------------------------------------------- /images/model-attempt2-classify2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt2-classify2.png -------------------------------------------------------------------------------- /images/model-attempt2-classify3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt2-classify3.png -------------------------------------------------------------------------------- /images/model-attempt2-classify4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt2-classify4.png -------------------------------------------------------------------------------- /images/model-attempt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt2.png -------------------------------------------------------------------------------- /images/model-attempt3-classify1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt3-classify1.png -------------------------------------------------------------------------------- /images/model-attempt3-classify2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt3-classify2.png -------------------------------------------------------------------------------- /images/model-attempt3-classify3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt3-classify3.png -------------------------------------------------------------------------------- /images/model-attempt3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/model-attempt3.png -------------------------------------------------------------------------------- /images/new-image-classification-dataset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/new-image-classification-dataset.png -------------------------------------------------------------------------------- /images/new-image-classification-model-attempt1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/new-image-classification-model-attempt1.png -------------------------------------------------------------------------------- /images/new-image-classification-model-attempt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/new-image-classification-model-attempt2.png -------------------------------------------------------------------------------- /images/new-image-classification-model-attempt3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/new-image-classification-model-attempt3.png -------------------------------------------------------------------------------- /images/new-image-classification-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/new-image-classification-model.png -------------------------------------------------------------------------------- /images/trained-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/trained-models.png -------------------------------------------------------------------------------- /images/upload-pretrained-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/humphd/have-fun-with-machine-learning/d0418772e744593b4d587f2bbaada9a3b0d78b60/images/upload-pretrained-model.png -------------------------------------------------------------------------------- /src/alexnet-customized.prototxt: -------------------------------------------------------------------------------- 1 | # AlexNet - customized for fine tuning AlexNet to 2 categories 2 | name: "AlexNet" 3 | layer { 4 | name: "train-data" 5 | type: "Data" 6 | top: "data" 7 | top: "label" 8 | transform_param { 9 | mirror: true 10 | crop_size: 227 11 | } 12 | data_param { 13 | batch_size: 128 14 | } 15 | include { stage: "train" } 16 | } 17 | layer { 18 | name: "val-data" 19 | type: "Data" 20 | top: "data" 21 | top: "label" 22 | transform_param { 23 | crop_size: 227 24 | } 25 | data_param { 26 | batch_size: 32 27 | } 28 | include { stage: "val" } 29 | } 30 | layer { 31 | name: "conv1" 32 | type: "Convolution" 33 | bottom: "data" 34 | top: "conv1" 35 | param { 36 | lr_mult: 1 37 | decay_mult: 1 38 | } 39 | param { 40 | lr_mult: 2 41 | decay_mult: 0 42 | } 43 | convolution_param { 44 | num_output: 96 45 | kernel_size: 11 46 | stride: 4 47 | weight_filler { 48 | type: "gaussian" 49 | std: 0.01 50 | } 51 | bias_filler { 52 | type: "constant" 53 | value: 0 54 | } 55 | } 56 | } 57 | layer { 58 | name: "relu1" 59 | type: "ReLU" 60 | bottom: "conv1" 61 | top: "conv1" 62 | } 63 | layer { 64 | name: "norm1" 65 | type: "LRN" 66 | bottom: "conv1" 67 | top: "norm1" 68 | lrn_param { 69 | local_size: 5 70 | alpha: 0.0001 71 | beta: 0.75 72 | } 73 | } 74 | layer { 75 | name: "pool1" 76 | type: "Pooling" 77 | bottom: "norm1" 78 | top: "pool1" 79 | pooling_param { 80 | pool: MAX 81 | kernel_size: 3 82 | stride: 2 83 | } 84 | } 85 | layer { 86 | name: "conv2" 87 | type: "Convolution" 88 | bottom: "pool1" 89 | top: "conv2" 90 | param { 91 | lr_mult: 1 92 | decay_mult: 1 93 | } 94 | param { 95 | lr_mult: 2 96 | decay_mult: 0 97 | } 98 | convolution_param { 99 | num_output: 256 100 | pad: 2 101 | kernel_size: 5 102 | group: 2 103 | weight_filler { 104 | type: "gaussian" 105 | std: 0.01 106 | } 107 | bias_filler { 108 | type: "constant" 109 | value: 0.1 110 | } 111 | } 112 | } 113 | layer { 114 | name: "relu2" 115 | type: "ReLU" 116 | bottom: "conv2" 117 | top: "conv2" 118 | } 119 | layer { 120 | name: "norm2" 121 | type: "LRN" 122 | bottom: "conv2" 123 | top: "norm2" 124 | lrn_param { 125 | local_size: 5 126 | alpha: 0.0001 127 | beta: 0.75 128 | } 129 | } 130 | layer { 131 | name: "pool2" 132 | type: "Pooling" 133 | bottom: "norm2" 134 | top: "pool2" 135 | pooling_param { 136 | pool: MAX 137 | kernel_size: 3 138 | stride: 2 139 | } 140 | } 141 | layer { 142 | name: "conv3" 143 | type: "Convolution" 144 | bottom: "pool2" 145 | top: "conv3" 146 | param { 147 | lr_mult: 1 148 | decay_mult: 1 149 | } 150 | param { 151 | lr_mult: 2 152 | decay_mult: 0 153 | } 154 | convolution_param { 155 | num_output: 384 156 | pad: 1 157 | kernel_size: 3 158 | weight_filler { 159 | type: "gaussian" 160 | std: 0.01 161 | } 162 | bias_filler { 163 | type: "constant" 164 | value: 0 165 | } 166 | } 167 | } 168 | layer { 169 | name: "relu3" 170 | type: "ReLU" 171 | bottom: "conv3" 172 | top: "conv3" 173 | } 174 | layer { 175 | name: "conv4" 176 | type: "Convolution" 177 | bottom: "conv3" 178 | top: "conv4" 179 | param { 180 | lr_mult: 1 181 | decay_mult: 1 182 | } 183 | param { 184 | lr_mult: 2 185 | decay_mult: 0 186 | } 187 | convolution_param { 188 | num_output: 384 189 | pad: 1 190 | kernel_size: 3 191 | group: 2 192 | weight_filler { 193 | type: "gaussian" 194 | std: 0.01 195 | } 196 | bias_filler { 197 | type: "constant" 198 | value: 0.1 199 | } 200 | } 201 | } 202 | layer { 203 | name: "relu4" 204 | type: "ReLU" 205 | bottom: "conv4" 206 | top: "conv4" 207 | } 208 | layer { 209 | name: "conv5" 210 | type: "Convolution" 211 | bottom: "conv4" 212 | top: "conv5" 213 | param { 214 | lr_mult: 1 215 | decay_mult: 1 216 | } 217 | param { 218 | lr_mult: 2 219 | decay_mult: 0 220 | } 221 | convolution_param { 222 | num_output: 256 223 | pad: 1 224 | kernel_size: 3 225 | group: 2 226 | weight_filler { 227 | type: "gaussian" 228 | std: 0.01 229 | } 230 | bias_filler { 231 | type: "constant" 232 | value: 0.1 233 | } 234 | } 235 | } 236 | layer { 237 | name: "relu5" 238 | type: "ReLU" 239 | bottom: "conv5" 240 | top: "conv5" 241 | } 242 | layer { 243 | name: "pool5" 244 | type: "Pooling" 245 | bottom: "conv5" 246 | top: "pool5" 247 | pooling_param { 248 | pool: MAX 249 | kernel_size: 3 250 | stride: 2 251 | } 252 | } 253 | layer { 254 | name: "fc6" 255 | type: "InnerProduct" 256 | bottom: "pool5" 257 | top: "fc6" 258 | param { 259 | lr_mult: 1 260 | decay_mult: 1 261 | } 262 | param { 263 | lr_mult: 2 264 | decay_mult: 0 265 | } 266 | inner_product_param { 267 | num_output: 4096 268 | weight_filler { 269 | type: "gaussian" 270 | std: 0.005 271 | } 272 | bias_filler { 273 | type: "constant" 274 | value: 0.1 275 | } 276 | } 277 | } 278 | layer { 279 | name: "relu6" 280 | type: "ReLU" 281 | bottom: "fc6" 282 | top: "fc6" 283 | } 284 | layer { 285 | name: "drop6" 286 | type: "Dropout" 287 | bottom: "fc6" 288 | top: "fc6" 289 | dropout_param { 290 | dropout_ratio: 0.5 291 | } 292 | } 293 | layer { 294 | name: "fc7" 295 | type: "InnerProduct" 296 | bottom: "fc6" 297 | top: "fc7" 298 | param { 299 | lr_mult: 1 300 | decay_mult: 1 301 | } 302 | param { 303 | lr_mult: 2 304 | decay_mult: 0 305 | } 306 | inner_product_param { 307 | num_output: 4096 308 | weight_filler { 309 | type: "gaussian" 310 | std: 0.005 311 | } 312 | bias_filler { 313 | type: "constant" 314 | value: 0.1 315 | } 316 | } 317 | } 318 | layer { 319 | name: "relu7" 320 | type: "ReLU" 321 | bottom: "fc7" 322 | top: "fc7" 323 | } 324 | layer { 325 | name: "drop7" 326 | type: "Dropout" 327 | bottom: "fc7" 328 | top: "fc7" 329 | dropout_param { 330 | dropout_ratio: 0.5 331 | } 332 | } 333 | layer { 334 | name: "fc9" 335 | type: "InnerProduct" 336 | bottom: "fc7" 337 | top: "fc9" 338 | param { 339 | lr_mult: 1 340 | decay_mult: 1 341 | } 342 | param { 343 | lr_mult: 2 344 | decay_mult: 0 345 | } 346 | inner_product_param { 347 | num_output: 2 348 | weight_filler { 349 | type: "gaussian" 350 | std: 0.01 351 | } 352 | bias_filler { 353 | type: "constant" 354 | value: 0 355 | } 356 | } 357 | } 358 | layer { 359 | name: "accuracy" 360 | type: "Accuracy" 361 | bottom: "fc9" 362 | bottom: "label" 363 | top: "accuracy" 364 | include { stage: "val" } 365 | } 366 | layer { 367 | name: "loss" 368 | type: "SoftmaxWithLoss" 369 | bottom: "fc9" 370 | bottom: "label" 371 | top: "loss" 372 | exclude { stage: "deploy" } 373 | } 374 | layer { 375 | name: "softmax" 376 | type: "Softmax" 377 | bottom: "fc9" 378 | top: "softmax" 379 | include { stage: "deploy" } 380 | } 381 | -------------------------------------------------------------------------------- /src/classify-samples.py: -------------------------------------------------------------------------------- 1 | # python classify-samples.py -c ~/repos/caffe/ -m ../data/googlenet-dolphins-and-seahorses 2 | 3 | import numpy as np 4 | import os 5 | import sys 6 | import argparse 7 | 8 | class ImageClassifier: 9 | def __init__(self, model_dir): 10 | self.model_dir = model_dir 11 | deploy_file = os.path.join(model_dir, 'deploy.prototxt') 12 | weights_file = os.path.join(model_dir, 'snapshot_iter_90.caffemodel') 13 | self.net = caffe.Net(deploy_file, caffe.TEST, weights=weights_file) 14 | 15 | def setup(self): 16 | mean_file = os.path.join(self.model_dir, 'mean.binaryproto') 17 | labels_file = os.path.join(self.model_dir, 'labels.txt') 18 | 19 | self.transformer = caffe.io.Transformer({'data': self.net.blobs['data'].data.shape}) 20 | self.transformer.set_transpose('data', (2, 0, 1)) 21 | self.transformer.set_raw_scale('data', 255) 22 | self.transformer.set_channel_swap('data', (2, 1, 0)) 23 | 24 | # set mean pixel 25 | with open(mean_file, 'rb') as infile: 26 | blob = caffe_pb2.BlobProto() 27 | blob.MergeFromString(infile.read()) 28 | if blob.HasField('shape'): 29 | blob_dims = blob.shape 30 | assert len(blob_dims) == 4, 'Shape should have 4 dimensions - shape is %s' % blob.shape 31 | elif blob.HasField('num') and blob.HasField('channels') and \ 32 | blob.HasField('height') and blob.HasField('width'): 33 | blob_dims = (blob.num, blob.channels, blob.height, blob.width) 34 | else: 35 | raise ValueError('blob does not provide shape or 4d dimensions') 36 | pixel = np.reshape(blob.data, blob_dims[1:]).mean(1).mean(1) 37 | self.transformer.set_mean('data', pixel) 38 | 39 | # This is overkill here, since we only have 2 labels, but here's how we might read them. 40 | # Later we'd grab the label we want based on position (e.g., 0=dolphin, 1=seahorse) 41 | self.labels = np.loadtxt(labels_file, str, delimiter='\n') 42 | 43 | def classify(self, fullpath): 44 | # Load the image from disk using caffe's built-in I/O module 45 | image = caffe.io.load_image(fullpath) 46 | # Preprocess the image into the proper format for feeding into the model 47 | self.net.blobs['data'].data[...] = self.transformer.preprocess('data', image) 48 | # Run the image's pixel data through the network 49 | out = self.net.forward() 50 | # Extract the probabilities of our two categories from the final layer 51 | softmax_layer = out['softmax'] 52 | # Here we're converting to Python types from ndarray floats 53 | dolphin_prob = softmax_layer.item(0) 54 | seahorse_prob = softmax_layer.item(1) 55 | 56 | # Print the results. I'm using labels 57 | label = self.labels[0] if dolphin_prob > seahorse_prob else self.labels[1] 58 | filename = os.path.basename(fullpath) 59 | print '%s is a %s dolphin=%.3f%% seahorse=%.3f%%' % (filename, label, dolphin_prob*100, seahorse_prob*100) 60 | 61 | def setup_caffe(caffe_root): 62 | # Load Caffe's Python interface from the specified path 63 | sys.path.insert(0, os.path.join(caffe_root, 'python')) 64 | global caffe 65 | global caffe_pb2 66 | import caffe 67 | from caffe.proto import caffe_pb2 68 | 69 | # Set Caffe to use CPU mode so this will work on as many machines as possible. 70 | caffe.set_mode_cpu() 71 | 72 | def main(): 73 | parser = argparse.ArgumentParser( 74 | description='Classify images of dolphins and seahorses using trained Caffe model' 75 | ) 76 | parser.add_argument('-c', '--caffe_root', help='CAFFE_ROOT dir, if not defined in env') 77 | parser.add_argument('-m', '--model_dir', help='Trained model dir, downloaded from DIGITS') 78 | parser.add_argument('-d', '--images_dir', help='Directory of images to classify') 79 | 80 | args = parser.parse_args() 81 | 82 | # Prefer $CAFFE_ROOT in the env if it exists, otherwise get from args 83 | caffe_root = os.getenv('CAFFE_ROOT') or args.caffe_root 84 | if not caffe_root: 85 | print 'Error: Missing CAFFE_ROOT dir. Set env variable or pass via --caffe_root' 86 | parser.print_help() 87 | sys.exit(1) 88 | setup_caffe(caffe_root) 89 | 90 | model_dir = args.model_dir 91 | if not model_dir or not os.path.isdir(model_dir): 92 | print 'Error: Unable to find model files. Pass dir via --model_dir' 93 | parser.print_help() 94 | sys.exit(1) 95 | classifier = ImageClassifier(model_dir) 96 | classifier.setup() 97 | 98 | # Allow passing images dir, or use ../data/untrained-samples by default 99 | cwd = os.path.dirname(os.path.abspath(__file__)) 100 | untrained_samples = os.path.join(cwd, '..', 'data', 'untrained-samples') 101 | images_dir = args.images_dir or untrained_samples 102 | if not os.path.isdir(images_dir): 103 | print 'Error: Unable to find images for classification. Pass dir via --images_dir' 104 | parser.print_help() 105 | sys.exit(1) 106 | 107 | # Classify all images in images_dir using our trained network 108 | for filename in os.listdir(images_dir): 109 | if filename.endswith('.jpg'): 110 | classifier.classify(os.path.join(images_dir, filename)) 111 | 112 | if __name__ == '__main__': 113 | main() 114 | --------------------------------------------------------------------------------