0 1 00:00:00,870 --> 00:00:07,050 All right so now it's time to visualize our mean squared error cost function. 1 2 00:00:07,170 --> 00:00:13,830 Now I don't know about you but I can't look at these equations and picture what they actually look like 2 3 00:00:13,950 --> 00:00:14,760 in my head. 3 4 00:00:14,770 --> 00:00:19,890 I'm the kind of person who really needs a graph or a chart or something visual to really understand 4 5 00:00:19,890 --> 00:00:21,340 what's going on. 5 6 00:00:21,350 --> 00:00:30,240 So let me insert a subheading here with Markdown, two pound symbols and I'll just write "3D plot for the 6 7 00:00:30,300 --> 00:00:40,860 MSE cost function" and let's have a subheading here that reads "Make data for thetas". 7 8 00:00:42,030 --> 00:00:49,110 The first step in creating our plot is to create a whole range of theta values that we can use to calculate 8 9 00:00:49,200 --> 00:00:50,860 the mean squared error for. 9 10 00:00:51,150 --> 00:00:58,020 So remember I said that the cost function depends on the parameter values and not the data itself, 10 11 00:00:58,050 --> 00:01:03,060 not the x_5 or the y_5 arrays but it depends on the theta. 11 12 00:01:03,270 --> 00:01:06,600 That's what the cost function is trying to map. 12 13 00:01:06,600 --> 00:01:12,090 The first thing that I am going to do is I'm going to set the number of data points for our chart. 13 14 00:01:12,090 --> 00:01:15,010 I'm going to call this nr_thetas. 14 15 00:01:15,060 --> 00:01:16,200 It's gonna be a variable. 15 16 00:01:16,470 --> 00:01:19,690 And to start off with I'm going to set it equal to 5. 16 17 00:01:20,130 --> 00:01:23,300 And then I'm going to create our data points. 17 18 00:01:23,340 --> 00:01:27,390 So I'll create a variable called th_0, 18 19 00:01:27,390 --> 00:01:32,170 I'm going to set that equal to the return value from numpy's linspace function. 19 20 00:01:32,160 --> 00:01:37,200 That's gonna be "np.linspace()". 20 21 00:01:37,530 --> 00:01:40,810 And then we're gonna supply three inputs - 21 22 00:01:40,830 --> 00:01:41,760 start value, 22 23 00:01:41,760 --> 00:01:51,480 we're gonna say -1; stop value we're going to say is equal to 3, and a number of intervals, so the number 23 24 00:01:51,480 --> 00:01:57,220 of points that we're gonna create, I'm going gonna set that equal to "nr_thetas". 24 25 00:01:57,510 --> 00:02:06,780 So that way when we look at th_0, we see that it has 5 values it's an array of 1, 2, 3, 4, 5 25 26 00:02:06,990 --> 00:02:08,250 items. 26 27 00:02:08,790 --> 00:02:16,530 Then I'm going to copy this line of code here, paste it in and have the same thing for theta_1, theta_1 is also 27 28 00:02:16,530 --> 00:02:22,260 going to go from -1 to 3 and it's going to have 5 values. 28 29 00:02:22,260 --> 00:02:28,460 So what we've done here is we've created a one dimensional array, but we're gonna have a 3D plot, 29 30 00:02:28,490 --> 00:02:31,540 so we're going to need a two dimensional array. 30 31 00:02:31,560 --> 00:02:37,950 We're not plotting a line, we're plotting a surface and for these we need an array of two dimensions. 31 32 00:02:38,640 --> 00:02:45,560 The way we're gonna create a two dimensional array is by using numpy's mesh grid function. 32 33 00:02:45,560 --> 00:02:47,200 So we've done this before. 33 34 00:02:47,200 --> 00:02:53,430 And just to differentiate between the one dimensional and two dimensional variables I'll create another 34 35 00:02:53,430 --> 00:02:54,260 set. 35 36 00:02:54,300 --> 00:03:07,080 So I'm going to say "plot_t0, plot_t1 = np.meshgrid()", so meshgrid is the 36 37 00:03:07,080 --> 00:03:14,250 function that will give us a two dimensional array and we're are using this comma notation "plot_t0, 37 38 00:03:14,250 --> 00:03:21,400 plot_t1" as a way of unpacking a sequence because meshgrid will return to us a tuple. Meshgrid in 38 39 00:03:21,400 --> 00:03:28,950 turn needs two inputs, so it's gonna be theta 0 and theta 1. From these inputs it's going to create 39 40 00:03:29,250 --> 00:03:31,050 our 2D arrays. 40 41 00:03:31,380 --> 00:03:39,810 So if we look at "th_0.shape" we'll see it's a flat array, one dimensional with 5 41 42 00:03:39,840 --> 00:03:47,880 elements and if we compare that to "plot_t0.shape" then we see that it is a 5 by 42 43 00:03:47,880 --> 00:03:55,580 5 array. It's two dimensional It has 25 elements total so if I delete the ".shape" and 43 44 00:03:55,570 --> 00:04:02,410 I press Shift+Enter I can see it here it looks like this - 5 by 5, 25 elements total. 44 45 00:04:02,610 --> 00:04:10,470 Now it's time to calculate the mean squared error for all the theta 0 and all the theta 1 values 45 46 00:04:10,920 --> 00:04:13,050 in our range. 46 47 00:04:13,140 --> 00:04:21,570 So let me ask you this - do you think that the cost that we calculate will have one dimension or will 47 48 00:04:21,570 --> 00:04:30,720 it have two dimensions? And the answer is it's also gonna be two dimensional - the surface plot needs to 48 49 00:04:30,720 --> 00:04:37,140 know the mean squared error value for all the combinations of theta zeros and theta ones - we're gonna 49 50 00:04:37,140 --> 00:04:41,460 be plotting a surface not a line after all. 50 51 00:04:41,460 --> 00:04:48,390 So here's what we're gonna do - we're gonna start out with a two dimensional array of just zeros and then 51 52 00:04:48,390 --> 00:04:54,730 we're gonna calculate the mean squared error for each value in our array separately. 52 53 00:04:55,050 --> 00:05:01,110 This will give us a chance to learn about another Python programming technique, which is very, very common 53 54 00:05:01,680 --> 00:05:05,190 and that's using nested loops. 54 55 00:05:05,190 --> 00:05:12,510 Let's add this as a subheading actually, so I am going to change this to Markdown, three pound signs and then I'm going to say 55 56 00:05:14,160 --> 00:05:23,400 "Calc MSE using nested for loops". Going to add a few more cells down here. 56 57 00:05:23,400 --> 00:05:25,350 Keep working in the middle of the screen. 57 58 00:05:26,040 --> 00:05:33,780 And the first thing I'm going to do now is I'm going to create a two dimensional array just of zeroes, so 58 59 00:05:33,780 --> 00:05:38,910 numpy actually has a function called zeros which is perfect for this. 59 60 00:05:38,910 --> 00:05:44,070 All we need to do is supply the dimensions of the array that we want as an output. 60 61 00:05:44,070 --> 00:05:49,770 So the variable that's going to hold on to all our mean squared error calculations is going gonna be 61 62 00:05:49,770 --> 00:05:52,470 called "plot_cost" 62 63 00:05:52,680 --> 00:05:57,340 and that's gonna be equal to the return value from this zeros 63 64 00:05:57,390 --> 00:05:58,610 function that I mentioned. 64 65 00:05:58,860 --> 00:06:03,270 So "np.zeros()" 65 66 00:06:03,600 --> 00:06:07,350 and then inside the parentheses we supply the dimensions. 66 67 00:06:07,350 --> 00:06:10,620 So if I supply 2 by 3, 67 68 00:06:10,620 --> 00:06:16,590 So "2,3" and then say "plot_cost", 68 69 00:06:17,290 --> 00:06:21,600 I can see what a 2 by 3 array of zeros will look like. 69 70 00:06:21,610 --> 00:06:26,620 There'll be 3 columns and 2 rows, 2 rows 3 columns. 70 71 00:06:26,620 --> 00:06:28,660 If it's 5 by 5, 71 72 00:06:28,720 --> 00:06:33,670 on the other hand it'll be 5 rows and 5 columns. 72 73 00:06:33,670 --> 00:06:37,320 Now I'm not going to have a hardcoded 5 in here. 73 74 00:06:37,320 --> 00:06:46,600 Instead I'm going to use the "nr_thetas" variable and and feed it as an argument to 74 75 00:06:46,690 --> 00:06:48,530 our zeros function. 75 76 00:06:48,550 --> 00:06:54,130 So what I'm going to do is I'm going to supply a tuple, which you can see by the two parentheses and it's 76 77 00:06:54,130 --> 00:06:58,330 gonna be square, it's gonna be symmetrical, so it's going to have the same number of rows and the same 77 78 00:06:58,330 --> 00:07:00,100 number of columns. 78 79 00:07:00,220 --> 00:07:06,310 So it'll be "nr_thetas, nr_thetas". 79 80 00:07:06,430 --> 00:07:10,150 This is gonna be the input to our zeros function, 80 81 00:07:10,210 --> 00:07:17,080 so if I ever have to change my variable up here and update this cell then my plot_cost 81 82 00:07:17,080 --> 00:07:19,790 will update as well. 82 83 00:07:19,810 --> 00:07:23,470 Now it's time to talk about nested loops. 83 84 00:07:23,560 --> 00:07:31,010 Let me add that as a comment here, say "Nested loop practice". 84 85 00:07:31,030 --> 00:07:32,550 Let me show you what I mean. 85 86 00:07:32,560 --> 00:07:38,110 So to create a nested loop, we're going to use two for loops. 86 87 00:07:38,110 --> 00:07:45,550 So the first for loop it's gonna look like this, is gonna read "for i in range()" and then say the number 87 88 00:07:45,550 --> 00:07:52,330 3 between the parentheses, then colon, and then inside this for loop there'll be another for loop. 88 89 00:07:52,390 --> 00:08:01,700 So I'm going to write "for j in range(3):" and inside the second for loop, 89 90 00:08:01,870 --> 00:08:03,710 I'm going to put a print statement. 90 91 00:08:03,970 --> 00:08:14,470 So, let me print the string "Hello" and hit Shift+Enter and what we see is that hello is printed a total 91 92 00:08:14,560 --> 00:08:22,480 of 9 times so, three, three and three; 3 times 3 is 9. 92 93 00:08:22,480 --> 00:08:24,450 So this is how many times we print "Hello". 93 94 00:08:25,420 --> 00:08:29,440 Let's go a step further than just printing "Hello" nine times. 94 95 00:08:29,530 --> 00:08:35,020 Let me show you another way of doing the string interpolation with Python. 95 96 00:08:35,110 --> 00:08:39,760 This is a different way that we can work with the strings from what we've seen before. 96 97 00:08:39,850 --> 00:08:43,550 This particular method is called an "fstring". 97 98 00:08:43,600 --> 00:08:47,300 It's an fstring because there's an "f" right in front of the string. 98 99 00:08:47,500 --> 00:08:54,580 So it's gonna be "f" and then you have the single or double quotes and then within the string itself I'm 99 100 00:08:54,580 --> 00:09:05,290 going to say "value of i is" and then I'm going to use a set of curly braces and inside these curly 100 101 00:09:05,290 --> 00:09:09,720 braces, I can access a variable in my Python code. 101 102 00:09:09,760 --> 00:09:13,230 So for example I can access this variable 102 103 00:09:13,240 --> 00:09:16,990 i here, the iterator in my outer loop. So I'm going to 103 104 00:09:17,020 --> 00:09:18,140 say "i" 104 105 00:09:18,340 --> 00:09:25,510 in between the curly braces, and then I'm going to continue "and j is", 105 106 00:09:25,510 --> 00:09:28,640 and then another set of curly braces, 106 107 00:09:28,920 --> 00:09:33,740 and then inside these curly braces I'm going to actually put the value "j". 107 108 00:09:33,850 --> 00:09:41,860 So I'm accessing this variable, the second iterator the one for the inner for loop. And 108 109 00:09:41,880 --> 00:09:45,470 now I'm going to hit Shift+Enter', see what we get. 109 110 00:09:45,480 --> 00:09:53,250 So what we see here is that the values that are printed out from inside these curly braces change every 110 111 00:09:53,250 --> 00:10:00,640 time the loop is executed by having this special notation, the f in front and then the curly braces were 111 112 00:10:00,660 --> 00:10:07,180 able to tell Python that something inside this string should not be considered a string. 112 113 00:10:07,210 --> 00:10:12,630 Instead we're able to reference values that are stored in variables elsewhere. 113 114 00:10:12,630 --> 00:10:18,990 Now the other thing that's really, really cool about this particular pattern is that we can see what happened 114 115 00:10:19,230 --> 00:10:23,560 exactly when our loop executed 9 times. 115 116 00:10:23,610 --> 00:10:26,220 So before we had "Hello" printed 9 times. 116 117 00:10:26,250 --> 00:10:32,760 But this time we can see what's actually going on and how these i and j values are changing. When the loop 117 118 00:10:32,760 --> 00:10:34,050 first starts out, 118 119 00:10:34,390 --> 00:10:35,970 i is equal to 0, 119 120 00:10:36,090 --> 00:10:38,730 and j is equal to 0. 120 121 00:10:38,730 --> 00:10:47,370 And then what happens is that the inner loop executes three times, j becomes equal to 1 and then to 2. 121 122 00:10:47,400 --> 00:10:50,700 And after that it's done executing. 122 123 00:10:50,700 --> 00:10:54,000 And then the outer loop increments by 1. 123 124 00:10:54,030 --> 00:11:02,400 So i goes from 0 to 1, and then it's j's turn again; j goes from 0 to 2, executes 3 times 124 125 00:11:02,910 --> 00:11:05,370 and the whole thing repeats one final time. 125 126 00:11:06,180 --> 00:11:13,230 So one of the things you can probably already tell is how this might be applied to many, many different 126 127 00:11:13,230 --> 00:11:15,110 kinds of problems. 127 128 00:11:15,210 --> 00:11:24,750 In particular it goes very, very well with iterating through a 2D array or a 2D matrix. If you want to 128 129 00:11:24,750 --> 00:11:31,080 hit up every single value then you can go row by row and column by column. 129 130 00:11:31,320 --> 00:11:34,900 And this is exactly what we're going to do. 130 131 00:11:34,920 --> 00:11:40,620 The important thing with this sort of stuff as with all Python code is to make sure the indentation 131 132 00:11:40,950 --> 00:11:48,150 is correct and that way Python knows that this loop is indeed nested inside this first one.