Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This may or may not help you understand what's going
- // on in week one of the Stanford Machine Learning course
- // particulary if you're coming from Javascript programming.
- // The lecture deals with linear regression. You may remember
- // a concept called "line of best fit" from middle school.
- // A refresher: you have a set of points with x and y co-ordinates
- // and you have to draw a straight line through them. It doesn't
- // have to intersect all (or any) of the points, but it does need
- // to represent the general layout of your dataset. The idea is
- // that this will let you make guesses for the y position of a
- // new point when you only know the x or vice versa. It often
- // won't be 100% accurate, but it'll give you a good idea, and a
- // good understanding of the range of possible error it could have
- // made.
- // A linear regression algorithm is just a way to make a computer
- // figure out what the line of best fit for a given dataset is.
- // The linear regression algorithm I describe below comes from
- // the Stanford Machine Learning course on Coursera. I found it
- // to be a bit too mathsy, so I'm writing it out in code, which
- // may help some people understand.
- // Linear regression algorithms return a function that takes an
- // x and returns a guess for the corresponding y value, based on
- // the *specific* dataset you trained against. This function is
- // called a "model". There are many other kinds of models for
- // various other kinds of machine learning algorithms, but because
- // we're focusing on linear regression, this model will be the
- // function of a line. You may remember this as `y=mx+c` where
- // `m` is the slope of the line, and c is the horizontal offset of
- // the line, which you can think about as 'the value of the equation
- // when x is 0'. In the course, this is written as hθ1=θ1+θ2*x.
- (()=>{
- // You'll need to make lots of linear functions, so
- // lets make that easy.
- function prepareLinearFunction (theta1, theta2) {
- return x => theta1 + theta2 * x;
- }
- // We're assuming in this example that your dataset contains
- // two columns, and therefore we have two thetas. You could
- // generalise the above function to take more columns into
- // account, but for now, lets focus on just two columns.
- // Lets define a dataset to learn from. It is possible to
- // accurately model this dataset with a line, so we should
- // end up without any errors.
- const dataset = [
- [0,1],
- [1,3],
- [2,5],
- [3,7]
- ];
- // You'll notice that the 0th element of each inner array is
- // the index of that element. I could just use the index,
- // but I wanted to make it more obvious.
- // Next you'll need a "cost function", which is just a
- // fancy name for a function that measures how well a
- // model is performing at it's task. To calculate this,
- // the function needs both of the current thetas, and
- // the dataset it's training against
- function costFunction(theta1, theta2, dataset) {
- // Here we generate our model using the function we
- // wrote earlier, giving it our current theta values.
- const model = prepareLinearFunction(theta1,theta2);
- // We apply a procedure to our model for each row
- // in the dataset.
- return dataset.map((x)=>{
- // First we get the difference between what the
- // data row says about the mapping and what our
- // model says.
- const difference = model(x[0])-x[1];
- // We then return the square of that difference.
- return Math.pow(difference,2);
- }) // Now we have an array of the square of the difference
- // between each of the examples and what we predicted for
- // their x value. The following line sums that array, which
- // we divide by the length of the dataset, and return.
- .reduce((x,y)=>{return x+y},0)/dataset.length*2;
- }
- // Ok, now we have a cost function. Next up, we implement
- // "gradient descent", which is just a prodedure for modifying
- // our theta values in a way that will decrease the overall "cost"
- // (otherwise known as "error") of our model.
- function gradientDescent(learningRate, thetaOne, thetaTwo, dataset) {
- // First, we calculate the error of the model that our current
- // values of theta creates
- const error = costFunction(thetaOne, thetaTwo, dataset);
- // Then we use the error, the learning rate to update each theta
- // thetas must be updated simultaneously - don't get into a
- // situation where you update one and you use that new theta
- // to update your next theta. It would probably work, but it's
- // not proper gradient descent.
- /*const newThetas = [thetaOne, thetaTwo].map(function(theta){
- // Note - I don't know how to calculate derivative of the cost
- // function yet. It wasn't covered in this lecture. It's a
- // measure of how far away your thetas are from a "local minimum"
- // in error, it's what guides your thetas towards finding their
- // correct values.
- theta * learningRate * derivitave * error;
- });*/
- //please excuse this hackery nonsense
- const model = prepareLinearFunction(thetaOne,thetaTwo);
- const a = dataset.map((x) => {
- return model(x[0])-x[1];
- }).reduce((x,y) => {return x + y}, 0)/dataset.length;
- const b = dataset.map((x) => {
- return (model(x[0])-x[1])*x[0];
- }).reduce((x,y) => {return x + y}, 0)/dataset.length;
- const newThetas = [
- thetaOne - learningRate * a,
- thetaTwo - learningRate * b
- ];
- debugger;
- return { newThetas, error };
- }
- // Lets run through the whole training process
- (function linearRegress(dataset){
- // Initialize thetas to zero. It's not important that
- // they're zero, they could start out as any number,
- // but zero works just fine.
- const initialThetaOne = 0;
- const initialThetaTwo = 0;
- const learningRate = 0.2;
- // Create a named function so that you can recurse
- // through the values of theta that gradient descent gives
- // you until you converge.
- (function reg(learningRate, thetaOne, thetaTwo, dataset) {
- console.log(`Current thetas: ${thetaOne}, ${thetaTwo}`);
- // Run gradient descent
- const {error,newThetas} = gradientDescent(learningRate, thetaOne, thetaTwo, dataset);
- console.log(`Those thetas gave us an error of ${error}.`);
- if (error < 0.00001) {
- // Our error is zero! That means our thetas have
- // "converged", or "settled" on values that make sense
- // for the dataset. Note that because our dataset fits
- // perfectly on a line, we can check that our thetas
- // match exactly. When your dataset is always going to
- // have a certain amount of error in it (i.e. when the
- // data doesn't exactly match any straight line), you'll
- // have to check tolerate some difference.
- console.log(`Convergeance! Line of best fit is: y = ${thetaOne} + ${thetaTwo}x.`);
- process.exit();
- }
- // Recursively call this function with updated thetas until
- // convergeance happens and the condition above is met
- console.log("Running gradient descent again to reduce the error.")
- reg(learningRate, newThetas[0], newThetas[1], dataset);
- })(learningRate, initialThetaOne, initialThetaTwo, dataset);
- })(dataset);
- })();
Advertisement
Add Comment
Please, Sign In to add comment