back to indexLive coding 13
Chapters
0:0 Introduction and Questions
5:15 MultiTask Classification Notebook
7:40 Good fastai tutorials
8:30 DataBlock API
12:35 How does ImageBlock, get_image_files work
15:15 How is aug_transforms working
17:30 Converting ImageDataLoaders to DataBlock
22:8 In PyTorch DataLoaders, what happens at the last batch?
23:23 Step2: Make DataBlock spit three things
27:30 Modifying get_y to send as two inputs
32:0 Looking into Dataset objects
33:50 Can we have multiple get_items?
35:20 Hacky notebook using data frames for creating DataBlock
39:40 How does TransformBlock and ImageBlock works
49:30 Looking at the source code of TransformBlock, DataBlock code
54:10 Dataset, Dataloaders discussion
58:30 Defining DataBlock for Multi-task classification notebook
65:5 Sneak peek into how the multi-task model is trained
00:00:21.280 |
And Nick is back from his tour of Tropical Islands. 00:00:31.600 |
Well, when you're at Queensland, Tropical Islands are easy. 00:00:48.680 |
Yeah, I'm at the Children's Health Queensland Hospital 00:00:58.240 |
Anybody got anything they wanted to talk about or ask about 00:01:03.840 |
is I've made things much easier in the horrible direction 00:01:08.600 |
All right, sounds like I should dive in then. 00:01:14.000 |
I know there was, like, as I rather expected, 00:01:22.960 |
and quite fair enough, some concern on the forum 00:01:26.000 |
about how complicated and weird this all is, which is partly 00:01:31.880 |
because we're kind of, like, jumping into stuff we'd normally 00:01:35.000 |
And so we haven't quite got all the necessary background here. 00:01:41.160 |
And so, yeah, don't worry if you're feeling a little at sea. 00:01:51.040 |
start to see, like, some stuff you could look into 00:02:15.120 |
Yes, I would absolutely like to do a part two this year. 00:02:18.720 |
And the fact that you're asking that question 00:02:20.600 |
means that you are not up to date on our Discord. 00:02:23.560 |
So you should definitely join our Discord channel 00:02:27.200 |
about doing a conference, or an unconference, 00:02:32.240 |
to go with part two at the end of the year in Queensland. 00:02:38.400 |
And a lot of folks from places that are not in Australia 00:02:43.000 |
are saying they would be up for coming here for that. 00:02:52.280 |
trying to do it all in a very COVID-safe way of outdoor, 00:02:56.080 |
and masked, and people tested ahead of time, and stuff. 00:03:06.520 |
We don't have dates, or a syllabus, or anything 00:03:11.440 |
to have something awesome maybe towards the end of the year 00:03:19.920 |
where we can get to know each other a bit better, 00:03:23.320 |
and get to know fast AI and deep learning a bit better. 00:03:42.160 |
The fact that I'm doing it this week is interesting 00:03:45.640 |
because, yeah, it has meant I've got less time to actually work 00:03:51.920 |
on the course, but I also feel like the stuff we're doing, 00:03:55.440 |
perhaps, is stuff I can use in the next lesson, 00:04:07.760 |
But then I certainly plan to continue doing them 00:04:17.240 |
We all know everything, I guess, and then we'll stop. 00:04:21.040 |
At which point, there'll be more things to know, so yeah. 00:04:37.000 |
My issue is, what about when we get to the point 00:04:48.120 |
But then we just start doing it all in a different language. 00:04:57.760 |
I think this is my fifth year of doing fast AI courses, 00:05:01.280 |
and I'm still trying to complete the part one. 00:05:21.400 |
I am just so happy about how this worked out, to be honest. 00:05:26.240 |
Although, spoiler alert, it didn't turn out to help our score. 00:05:34.680 |
But I was so happy at how the whole process turned out. 00:05:38.920 |
But I kind of want to show you how I got there, as well as 00:05:45.880 |
And yeah, as soon as I kind of turned off Zoom last time, 00:05:50.680 |
and I went for a walk, and then as soon as I did that, 00:05:53.520 |
I was like, oh, no, of course I know how we should do this. 00:06:02.160 |
So let me explain what we're going to try to do. 00:06:05.680 |
We are going to try to predict two things, the disease 00:06:23.600 |
And the first thing will be to create a pair of data loaders 00:06:29.920 |
For each image, they will have connected two things to them, 00:06:39.320 |
So let me kind of show you how I got to that step. 00:06:44.400 |
So the step one that I took was to, first of all, 00:06:48.120 |
try to replicate what we've already had before. 00:06:58.600 |
which is the highest level least flexible function. 00:07:10.200 |
And trying to predict two things is not standard enough for it. 00:07:18.040 |
And there's a lot of really good tutorials on docs.fast.ai. 00:07:27.600 |
And because it's been ages since I've done any of this, 00:07:37.960 |
But for example, there is a data block tutorial. 00:07:45.560 |
It goes through all the layers of different ways 00:07:53.760 |
This Siamese tutorial is another really good one. 00:07:58.560 |
And the other thing that I looked at was the actual API docs. 00:08:06.120 |
this is actually probably what I found the most useful 00:08:11.640 |
There's lots of great examples in the documentation. 00:08:17.040 |
you know how it is, you come back to something a couple 00:08:24.200 |
And so my experience as a customer of my documentation 00:08:28.800 |
So I can definitely suggest checking all that out. 00:08:37.000 |
before, we were using image data loaders.from folder. 00:08:40.080 |
So if we just do the double question mark trick, 00:09:04.240 |
step one that I did was to replicate exactly what we had 00:09:13.040 |
And for this, there's actually so many good examples 00:09:25.480 |
We can basically say, OK, for the data block, 00:09:35.080 |
The labeling function will be the parent folder. 00:10:06.760 |
So data loaders is then a data block.data loaders. 00:10:24.880 |
into to grab the things that will be passed to these blocks 00:10:50.400 |
to pass that path into the getImageFiles function, 00:11:09.680 |
OK, so the block, you have an image block, category block. 00:11:21.400 |
is going to be able to feed both those blocks? 00:11:32.000 |
be to read the documentation about those blocks 00:11:42.560 |
As you can see, they're used all over the place, right? 00:11:44.720 |
So you can start with this tutorial, or this tutorial, 00:11:55.080 |
Yeah, the actual-- sorry, this is not good documentation. 00:12:05.240 |
because it's basically all it does, because we should fix 00:12:12.880 |
that, I guess, because there's so many tutorials. 00:12:16.840 |
I mean, as you can see, I guess the reason I never really 00:12:19.680 |
wrote drops, right, is that it's literally a single line of code. 00:12:36.120 |
calls class.create, where class is P-I-L image. 00:12:44.920 |
So to find out what's actually going to be called by it, 00:12:49.600 |
you can go P-I-L image.create, and you can see 00:13:01.640 |
can be a path, or a string, or various other things. 00:13:09.680 |
can either run it to see what it comes out with, 00:13:11.880 |
or let's do that so we could just run get image files, 00:13:16.000 |
passing in the thing it's going to be given, which is the path. 00:13:20.760 |
And so as you can see, it's a bunch of paths. 00:13:28.080 |
copy that, and it's going to be passed into this function. 00:13:43.400 |
look at the tutorials, which tell you what to do. 00:14:05.160 |
like it gets transformed later after reading, right? 00:14:08.880 |
Yeah, here we've got the batch transform, yeah. 00:14:12.440 |
But in the image block, because right now, we 00:14:22.200 |
It gets changed from an int tensor to a float tensor 00:14:27.520 |
Yeah, that's a fairly subtle thing, but that's right. 00:14:39.720 |
Image equals-- and we look at, like, np.array image. 00:14:47.480 |
It's actually stored as bytes, or UN8, as they call it, 00:14:54.440 |
So yes, this is going to add a batch transform that's 00:15:00.200 |
going to turn that into a float tensor, which 00:15:06.040 |
We could run it here, I expect, to float tensor. 00:15:17.840 |
is 224, which is like a square image, correct? 00:15:52.720 |
can see examples of the augmentations it does. 00:15:55.760 |
So it tells you a list of all the augmentations 00:15:58.760 |
And here's some examples of what they look like. 00:16:02.960 |
These augmentations would happen after the integer float 00:16:13.080 |
operate on the entire batch, and some operate 00:16:18.240 |
So the ones in batch transforms operate on the whole batch, 00:16:27.880 |
because they operate on a batch, before you get there, 00:16:34.800 |
So this resizes everything to the same shape. 00:16:38.680 |
And then this does these various different types of data 00:16:44.160 |
And one of the key pieces of data augmentation it does 00:16:47.440 |
is to randomly zoom into a subset of the image, 00:16:51.760 |
as you can see in these various examples here. 00:17:05.920 |
would be reading your images from a data frame? 00:17:14.400 |
So yeah, I'm kind of skipping over quite a bit of this, 00:17:17.360 |
because it's super well covered in the tutorials. 00:17:22.040 |
So I don't want to say stuff that you can very easily read. 00:17:26.040 |
isn't as well covered in the tutorials, and it's kind of new. 00:17:39.840 |
is this is just the same thing that we have in lesson one. 00:17:43.880 |
And it's doing exactly the same thing as my image data 00:17:51.760 |
loaders.from folder, but just turn it into data block. 00:18:05.200 |
And so let's go copy, and I want this all to run as fast as 00:18:17.560 |
Do you-- when you make this data loader thing, 00:18:27.520 |
do you try to make sure that the shape that it's outputting 00:18:31.560 |
is what you need for your model, or that's later? 00:18:50.160 |
to bring this back down to 128, so it's super fast. 00:18:53.920 |
And so I just want to get the maximum iteration speed here. 00:19:09.400 |
OK, so this is going to run in under 20 seconds, which 00:19:22.320 |
You want something that you can test in under about 20 seconds, 00:19:45.680 |
You know that this is what you need for that model. 00:20:05.080 |
because maybe different models are kind of trying 00:20:07.600 |
to predict different types of stuff, potentially? 00:20:13.360 |
Like, some might have the shape of the target. 00:20:24.320 |
is the thing that we're going to be covering in a moment. 00:20:37.120 |
On the data block, you randomly select the amount of records 00:20:44.560 |
or the amount of the batch size that you're going to process. 00:20:51.360 |
The batch size is actually selected in the .dataLetters 00:20:59.400 |
So what is the guarantee that every single one 00:21:09.400 |
Is there any way to know that every single one will be-- 00:21:13.720 |
I mean, well, yes, except that we're randomly 00:21:21.880 |
But every single one will go through the learner. 00:21:29.080 |
will go through our learner because we randomly shuffle them 00:21:38.760 |
guaranteed to see every example that is trained just once. 00:21:47.480 |
That's what one epoch means, is look at everything once. 00:21:50.280 |
And so if we put two there, it would look at everything twice. 00:22:03.080 |
but what actually happens for the last batch? 00:22:08.360 |
And this is actually not the PyTorch data loader, 00:22:12.680 |
So we have our own data loader, although in the next version, 00:22:16.120 |
we're likely to replace it with the fast.ai one. 00:22:20.760 |
If drop last is true, then it deletes the last batch. 00:22:24.920 |
And if it's false, then it includes the last batch. 00:22:27.560 |
And the reason that's interesting is that the last batch may not 00:22:34.920 |
For the validation set, it always keeps the last batch. 00:22:39.080 |
It's super important to shuffle the transfer. 00:22:44.560 |
will mess around with the data loaders or do something 00:22:49.960 |
you might get very poor training performance. 00:22:57.960 |
Yeah, trying to get all those details, right? 00:23:03.600 |
going to replace it with the PyTorch data loaders? 00:23:17.360 |
Is to just get it working exactly like before. 00:23:27.320 |
to make sure I got about the same error rate, and I did. 00:23:29.760 |
So then I was happy that, OK, I'm matching what we had before. 00:23:38.440 |
so that the data block spits out three things, which 00:23:44.480 |
The category of disease and the category of rice type. 00:23:50.000 |
So to get it to spit out an image and two categories, 00:23:57.920 |
We say we want three blocks, an image, and two categories. 00:24:13.160 |
And actually, the way I did it was a bit ugly. 00:24:16.680 |
And since then, I thought of a better way of doing it, 00:24:20.560 |
is we should create a dict that maps from image ID to variety. 00:24:25.520 |
And then our function will just be to look that up, right? 00:24:36.800 |
OK, and it's going to be a dict comprehension. 00:24:40.800 |
So we're going to loop through the rows in df dot iter items. 00:25:00.400 |
Now, I always forget what these differences are. 00:25:04.760 |
Column name comma series pair, returning a tuple 00:25:22.520 |
I think like this iter tuples is the fastest one. 00:25:28.480 |
But this is not very big, so let's keep it simple. 00:25:42.120 |
Another thing we could do is make the image ID the index, 00:25:50.840 |
and then you could actually jump straight into it. 00:25:54.400 |
But I think I'd rather not use pandas features. 00:25:59.160 |
because I think that'll make the explanation a little clearer. 00:26:08.720 |
And so what we want is the key will be the row's image ID, 00:26:36.000 |
And I'm just going to show you a little neat trick, which 00:26:53.480 |
calling a special magic method in Python called 00:27:05.200 |
It's so dynamic and flexible, like all the syntax sugar 00:27:09.160 |
is like behind the scenes just calling functions, basically. 00:27:21.040 |
a function that converts a file name into a variety. 00:27:35.200 |
and it's going to call each of those functions, which 00:27:56.680 |
And it says, oh, getY contains two functions, 00:27:59.440 |
but it should contain one, one for each target. 00:28:05.320 |
Well, if you think about it, we've said there's three blocks, 00:28:07.840 |
but we haven't told it how many of those blocks 00:28:10.200 |
are for the independent variable and how many 00:28:15.600 |
And the way we do that is to say the number of inputs equals-- 00:28:20.840 |
We have one input, and then the rest will be outputs. 00:28:31.120 |
And personally, before I jump to data loaders, 00:28:33.880 |
I first create data sets just to make sure they work. 00:28:44.000 |
So data sets are easier to debug than data loaders. 00:29:22.920 |
This path does not exist as a key in this dictionary, 00:29:36.360 |
so that we've got train images, bacterial leaf streak, 00:29:46.200 |
is being passed to the get items is being passed to get y. 00:29:54.000 |
Yeah, so we haven't kind of gone into the details of exactly 00:30:04.200 |
to jump into the nitty gritty, but it's a little bit-- 00:30:20.000 |
I always like to see people when they're, you know, seeable. 00:30:25.520 |
OK, so we're not going to use this trick after all. 00:30:29.320 |
We're going to create a function called get variety. 00:30:37.760 |
Yeah, let's create a function called get variety. 00:30:47.560 |
And so we're going to return image to variety. 00:31:08.600 |
Wait, we need image to variety, the dunder thing? 00:31:12.880 |
Oh, yeah, I'll just grab a bracket, actually, yes. 00:31:43.640 |
OK, and it also contains a dot valid data set. 00:31:48.400 |
OK, and so we can look at the zeroth thing in the training 00:32:02.160 |
And so then we can look at the image, for example. 00:32:09.000 |
OK, so what's happened here is that get image files returned 00:32:15.400 |
The first one got passed to image block, which, 00:32:19.680 |
as we saw earlier, got passed to pyo image dot create. 00:32:26.720 |
And that path name also got passed to a function 00:32:33.480 |
So let's say file name equals get image files. 00:32:39.680 |
And then the thing that we passed in, training path, 00:32:50.040 |
So it ended up calling pyo image dot create with that file name. 00:33:00.040 |
OK, it also called parent label with that file name. 00:33:10.080 |
And it also called get variety with that file name. 00:33:12.760 |
Jeremy, can we look at get variety one more time? 00:33:32.240 |
I did it the other way around, of building back up the path, 00:33:35.280 |
and then realized that that was kind of stupid. 00:33:44.200 |
OK, this could be too low level, but just let me know. 00:33:55.640 |
So it wouldn't make sense to have multiple get items, right? 00:34:04.360 |
It could be it could return a tuple, or a list, or an object, 00:34:19.400 |
Now, we don't need to get x, because image blocks just 00:34:26.520 |
If I needed something a bit more like I wanted to put more things 00:34:31.040 |
and get image files, like have it emit a tuple, 00:34:33.680 |
then would I have to make my own image block to ignore? 00:34:53.000 |
because nearly always there's a row of a database 00:34:56.400 |
table, or a path, or something has all the information 00:35:01.240 |
you need to go out and get the stuff with your get x's 00:35:07.920 |
That's like the central piece of information for each row. 00:35:11.280 |
And based on this information, you can read in text, 00:35:14.000 |
you can read in images, but specific to that one row. 00:35:21.360 |
because this is the version that uses a data frame. 00:35:24.400 |
So this is-- so the version that uses data frame-- 00:35:41.520 |
Let me just do this, and let me come to your question. 00:35:43.680 |
OK, so in this data block, I started out with a data frame, 00:35:57.720 |
as I passed in the data frame, it's going to get each row. 00:36:24.280 |
can do things like add in a prefix path and a suffix path, 00:36:27.680 |
and you can split it with a label delimiter and whatever. 00:36:36.000 |
and it checks what kind of thing you're passing in. 00:36:38.680 |
Basically, all it does is it calls getAtra to grab 00:36:54.480 |
colreader for context, like reading data frames. 00:37:03.120 |
Is this colreader function specifically for data frames? 00:37:08.000 |
I mean, it can work with anything, basically, 00:37:11.120 |
that you're-- so what it's doing here is it's saying, 00:37:24.600 |
But yeah, so basically here, getY is saying, OK, well, 00:37:27.280 |
let's return the index 1 field and the index 2 field. 00:37:42.680 |
so you can't pass a row of a database table to pailimage.create. 00:37:48.840 |
So getX is this function, which basically is going, 00:37:55.360 |
oh, it's going to be in the training path slash disease 00:38:04.120 |
So that's-- and then there's a special case for the test set, 00:38:09.240 |
because the test set things are not stored in subfolders 00:38:13.400 |
according to label, because we don't know the label. 00:38:18.440 |
So that's the-- as I said, this was more hacky. 00:38:30.120 |
Yeah, you can have-- yeah, it's totally flexible. 00:38:32.240 |
And I mean, seriously, Hamel, we have so many examples 00:38:37.200 |
of all of these patterns in the docs in the tutorials. 00:38:45.000 |
let's take a look at one, right, docs.faster.ai. 00:39:05.840 |
splitting based on columns in the database table. 00:39:11.480 |
And here's a co-reader using a label delimiter. 00:39:23.560 |
Yeah, so I think I'm at a point now where I actually 00:39:32.240 |
totally free to ask any super-weedy questions. 00:39:54.600 |
it's going to store a bunch of things you pass in. 00:39:56.880 |
It's going to store things called type transforms. 00:39:58.920 |
It's going to store things called item transforms. 00:40:00.800 |
It's going to store things called batch transforms. 00:40:06.680 |
which is to tensor, because PyTorch means tensors. 00:40:15.280 |
saw that that's defined as a transform block where 00:40:19.200 |
the type transforms is this and the batch transforms is this. 00:40:33.720 |
and don't pass any transforms, it won't do anything. 00:40:56.320 |
Let's do that separate cell so it gets a little bit easier 00:41:26.160 |
all it does is it takes the output of get image file 0 00:41:31.960 |
and turns it into a tuple containing one thing, which 00:41:51.480 |
When we train, we have batches containing inputs and outputs, 00:41:59.480 |
potentially multiple inputs and potentially multiple outputs. 00:42:02.200 |
So that's why indexing into this gives you back a tuple. 00:42:08.520 |
My question, the blocks can either be a list or a tuple? 00:42:26.340 |
So then we can do stuff to the first thing in the tuple. 00:42:44.160 |
So get x equals-- say let's get a lambda o dot name. 00:43:48.260 |
I wonder if something happened to my GPU server. 00:43:53.780 |
I mean, something has happened to my GPU server, clearly. 00:44:20.380 |
I'm very-- oh, I don't know what just happened. 00:44:42.900 |
I'm just looking at the log, see if anything just happened. 00:45:09.140 |
So you see what happened here is we got the first thing 00:45:18.500 |
from image files, which was this, and get x got its name. 00:45:26.540 |
So we could also do get y equals lambda o o dot parent, say. 00:45:42.500 |
It first went-- first, the thing went to the transform block, 00:45:52.820 |
So whatever get items got went to transform blocks. 00:46:00.060 |
Well, transform block doesn't do anything, right, at all, 00:46:07.140 |
So it's basically-- but the number of them you have 00:46:11.500 |
is the number of pipelines it's going to create. 00:46:32.700 |
And it's not quite the mental model you've got, I think. 00:47:04.140 |
Unless we say number of inputs equals 1, in which case 00:47:11.660 |
now we get x is just going to have to return one thing. 00:47:28.180 |
So you could even put it here instead, right? 00:47:43.620 |
So you could say, oh, well, this is actually-- 00:48:39.060 |
So it's now converted to the type it's meant to be. 00:48:49.180 |
I'm just curious how all the pieces interact. 00:49:11.140 |
Yeah, why don't we figure out what's going on here, 00:49:14.700 |
OK, so now we've got three transform blocks, two of them 00:49:34.500 |
if you look at the code of them, transform blocks 00:49:59.140 |
The data block is the thing that then going to go through 00:50:01.980 |
and say, OK, for each thing, call its type transforms, 00:50:05.700 |
and then call to tensor, and then call its item transforms, 00:50:08.580 |
and then data load of time, call its batch transforms. 00:50:12.980 |
So does that help answer your question, Hamill? 00:50:16.140 |
It's not that a transform block doesn't get called. 00:50:25.300 |
The first thing that gets called is type transforms. 00:50:37.100 |
The first thing that gets called is get x and get y, 00:50:40.100 |
and then the result of that is passed into type transforms. 00:50:47.020 |
so get x would be responsible for making sure 00:50:49.580 |
that you have a path that you can pass to pio-image.create. 00:50:55.460 |
So this whole path of what happens in a sequence 00:51:05.380 |
and it could do with some simplifying and documenting 00:51:21.060 |
But basically, when you call .datasets, really, 00:51:31.380 |
object passing in all of the type transforms to it. 00:51:43.740 |
get done by the data loader, not by the data sets. 00:51:51.780 |
And basically, the only reason there's quite a bit of code 00:51:55.180 |
in here is we try to make sure that if two different things 00:52:00.820 |
have the same type transforms, we merge them together 00:52:06.580 |
So there's some stuff to try to make sure this all just works. 00:52:16.820 |
because of some optimization you can do with the type transforms? 00:52:21.300 |
Because the type transforms, they're happening earlier. 00:52:34.460 |
are going to take tensors, or at least things that 00:52:48.780 |
that are going to create your data sets for you. 00:53:01.220 |
which are things like reshaping everything to the same size. 00:53:04.180 |
And batch transforms, which are things like data augmentation. 00:53:09.580 |
But you can have an item transform run on the GPU 00:53:23.260 |
going to run on the GPU because it's not a batch yet. 00:53:29.100 |
but that would be pretty weird because you really 00:53:32.060 |
don't need things to be in a batch before the GPU can 00:53:43.420 |
Assuming that you're using a GPU, I mean, this is OK. 00:53:54.420 |
I think this might be a callback which sticks things on the GPU. 00:53:57.820 |
So it just depends on whether things are before or after 00:54:02.820 |
Yeah, that's probably a bit of a distraction. 00:54:08.780 |
To kind of revise the difference between data set and data loader, 00:54:13.140 |
is it best to revisit the PyTorch documentation and kind of-- 00:54:24.380 |
And PyTorches data set is like literally it's an abstract class. 00:54:31.740 |
So a data set is something that you can index into. 00:54:35.300 |
And it returns a single tuple of your independent and dependent 00:54:40.140 |
That's what a data set is defined as by PyTorch. 00:54:51.860 |
The only thing you can do is iterate through it. 00:54:55.660 |
And it gives you a mini-batch, which is a tensor. 00:55:04.140 |
I guess I'm trying to understand the type transform thing, 00:55:09.340 |
why it has to be done in the data set before the data loader. 00:55:19.780 |
to have to have something you can go into and grab items, 00:55:26.940 |
That's the basic foundation of the PyTorch data model, 00:55:57.140 |
This is not the only way you could do this, right? 00:56:02.540 |
because we now have this thing that you can say like, 00:56:05.220 |
oh, Hamill, can you show me the 14th image and its label? 00:56:17.020 |
So yes, that's just a convenient thing, basically. 00:56:29.340 |
then it would just be one more step in the item transforms, 00:56:38.180 |
Yeah, your data sets would always just return a single thing, 00:56:42.300 |
or maybe two things, the get x and get y results. 00:56:46.540 |
And then your data loader would have to do more work, basically. 00:56:52.260 |
Which would be a perfectly OK way to do things as far as I 00:56:56.220 |
can tell that I think would be a little harder to debug 00:57:07.860 |
Is it like anything you want to do up front that 00:57:11.300 |
is like kind of uniform across your whole data set, 00:57:15.460 |
that you don't need to change at training time? 00:57:23.660 |
want to be able to index into it and look at that thing, really. 00:57:33.660 |
I'd say just chuck it somewhere and don't worry about it. 00:57:52.100 |
So you need to make sure that your type transform, when 00:57:55.860 |
you're working with fast AI, returns something 00:57:57.780 |
that is a tensor or going to be turned into a tensor. 00:58:12.500 |
it's a convenient thing that you understand to look at. 00:58:29.100 |
So let's replace it with the word image block. 00:59:01.340 |
Here's kind of something we want as our label, right? 00:59:24.260 |
This can't be turned into a tensor because it's a string. 00:59:35.780 |
we learned that what we do is we replace strings with integers 00:59:39.580 |
where that integer is a lookup into a vocabulary. 00:59:55.860 |
that is exactly what category block will do, right? 01:00:04.380 |
And so category block, it's got a type transform 01:00:15.580 |
categorize, which I'm not going to go into because it's not 01:00:21.620 |
But if you look up the documentation for categories, 01:00:29.500 |
find that the vocab is stored for these things. 01:00:35.380 |
So if we look at this at a high level, get items, get-- 01:00:39.900 |
By the way, just a moment, here's the vocab, right? 01:00:49.540 |
So get items gets us the rows or the examples 01:00:59.140 |
And then from get items, we use get y or get x 01:01:03.820 |
to transform it somehow so that we can pass it 01:01:13.700 |
And type transforms are things that can get triggered, right? 01:01:19.700 |
So they're doing a little bit something similar to get y, 01:01:26.980 |
Because these are very general things, right? 01:01:38.860 |
I will work if you can pass me a path to an image. 01:01:42.540 |
And this says, I will work if you pass me a string. 01:01:45.620 |
And so get x and get y then are responsible for ensuring 01:01:48.980 |
that you pass them a path and pass this one a string. 01:01:53.500 |
And get image files is already returning paths, 01:02:14.900 |
But let's have a look at-- so this is exactly the same. 01:02:26.060 |
OK, so this is exactly the same as what we just had, right? 01:02:30.620 |
And so then we can also then add the two things, which 01:02:33.020 |
is the item transforms and the batch transforms. 01:02:35.020 |
Some other time, we will talk about how it is that-- 01:02:38.100 |
how come this is not being applied to the categories? 01:02:43.380 |
For those of you interested in skipping ahead, 01:02:45.500 |
the secret is using fast calls type dispatch functionality. 01:02:55.460 |
these three different things-- image, we've got y1. 01:02:58.140 |
So Jeremy, if we had an image, if we had an image block 01:03:04.900 |
for our y's, for our targets, then item transform 01:03:13.260 |
And there's a-- have a look at the Siamese tutorial 01:03:17.220 |
on the fast.ai docs, because that has two images. 01:03:22.900 |
And if you think about it, any time we do segmentation, 01:03:27.340 |
The data augmentation is happening to x and y. 01:03:33.980 |
that have this kind of totally transparent ability 01:03:36.700 |
to do bounding boxes, segmentation, point clouds, 01:03:42.380 |
whatever as dependent variables, and have it all 01:03:51.940 |
OK, so now I can create data loaders from that. 01:04:04.940 |
And thanks to the magic of fast.ai, this is so cool. 01:04:12.700 |
It's actually auto labeling it with each of our categories. 01:04:16.620 |
So thanks to stuff we'll discuss later, basically this stuff 01:04:20.860 |
called type dispatch, fast.ai does a lot of things 01:04:26.340 |
automatically, even though I don't think I've ever explicitly 01:04:31.060 |
It just does because of how the API is designed. 01:04:40.700 |
different dependent variables, each one of which 01:05:05.580 |
All that stuff I did last time about messing around 01:05:12.180 |
All we need to do when we create our vision learner 01:05:21.860 |
how many outputs you want by how many levels are 01:05:27.380 |
But in this case, we've got something custom, right, 01:05:34.380 |
That's going to make the final matrix that it multiplies by 01:05:41.060 |
Now, then you basically need to tell it what loss function 01:05:50.540 |
we used to use a loss function for this called 01:05:54.300 |
So we're going to call that exact loss function 01:06:00.740 |
And we're going to compare that to the disease probabilities. 01:06:11.660 |
And we'll do the same thing for having an error rate, which 01:06:14.940 |
just looks at the first 10, the error rate for disease, 01:06:25.700 |
it's going to print out the disease and the variety error. 01:06:29.300 |
And the loss function will be the loss function 01:07:18.060 |
No, firstly, I handled all that for you automatically. 01:07:34.900 |
as to whether to decode it and whether to put 01:07:37.140 |
the final activation function on it and stuff like that. 01:07:42.740 |
In this case, because we used a custom loss function, 01:07:51.540 |
you would need to add a softmax if you wanted to. 01:08:03.260 |
I just needed which disease had the highest prediction. 01:08:11.420 |
going to be the same because that's a monotonic function. 01:08:26.540 |
Yeah, but you would only look at the first 10 01:08:36.740 |
So I was using TTA to do test plan augmentation. 01:08:41.540 |
And I stacked up and I did an ensemble of TTA. 01:08:44.460 |
And then I just did an argmax on the first 10. 01:08:55.380 |
In the architecture, you selected for ResNet 18, 128. 01:09:32.620 |
that might take a whole lecture to understand.