TypeScript版的吴恩达的机器学习课程练习-2
  1oBeqNAi2px1 2023年11月19日 81 0

这几天看完了逻辑回归相关的课程,听着的时候感觉还算顺利,但是在进行课程练习的过程中还是花费了较长的时间,因为我画出的图形和实际出题题目后面的图形有点不太一样,所以来来回回不断地调整参数。后面才发现和学习速度α以及梯度下降次数有很大的关系。


模型实现

具体的模型推到就不说了,具体推到过程在视频和相关的学习资料中已经说的很清楚了,那么直接通过TypeScript进行实现吧


假设函数

分为了三个函数,为了清晰拆分了三个函数,分别为HypothesisSigmoidPolynomial

// 实现的是1/(1+e^(-z))
async Sigmoid(pow: tf.Tensor<tf.Rank>) {
  const e = tf.fill([1, pow.shape[1] || 1], Math.E);
  const ones = tf.ones([1, pow.shape[1] || 1]);
  return ones.div(ones.add(e.pow(pow.mul(-1))));
}

// 实现的是θT*X
async Polynomial(feature: tf.Tensor<tf.Rank>, theta: tf.Tensor<tf.Rank>) {
  const ones = tf.ones([feature.shape[0], 1]);
  const data = tf.concat([ones, feature], 1);
  const powForE = theta.transpose().matMul(data.transpose());
  return powForE;
}

async Hypothesis(feature: tf.Tensor<tf.Rank>, theta: tf.Tensor<tf.Rank>) {
  return await this.Sigmoid(await this.Polynomial(feature, theta));
}


代价函数

因为题目中不需要再代价函数中实现lambda,所以这边就没写

  async Cost(
    feature: tf.Tensor<tf.Rank>,
    theta: tf.Tensor<tf.Rank>,
    y: tf.Tensor<tf.Rank>,
    lambda: number = 0
  ) {
    const yi = y.transpose();
    const h = await this.Hypothesis(feature, theta);
    const e = tf.fill([1, h.shape[1] || 1], Math.E);
    const ones = tf.ones([1, h.shape[1] || 1]);
    const a1 = yi.mul(-1).mul(h.log()); //-yi*log(h(xi))
    const a2 = ones.sub(yi).mul(ones.sub(h).log()); //(1-yi)*log(1-h(xi))
    let sumData = a1.sub(a2).sum();
    return sumData.div(feature.shape[0]).dataSync();
  }


梯度函数

这个方法实现的下降一次的方法,加入Lambdaθ进行正则化

  async Gradient(
    feature: tf.Tensor<tf.Rank>,
    theta: tf.Tensor<tf.Rank>,
    y: tf.Tensor<tf.Rank>,
    alpha: number,
    lambda: number = 0
  ) {
    const h = await this.Hypothesis(feature, theta);
    const ones = tf.ones([feature.shape[0], 1]);
    const featureData = tf.concat([ones, feature], 1);
    let sumData = h.transpose().sub(y).mul(featureData)
    let descent = sumData.sum(0, true).transpose().div(feature.shape[0])
    if (lambda > 0) {
      const thetaProcess = theta.mul(tf.zeros([1, 1]).concat(tf.ones([theta.shape[0] - 1, 1])));
      const data_Regularization = thetaProcess.mul(lambda / feature.shape[0])
      descent = descent.add(data_Regularization);
    }
    const data = theta.sub(descent.mul(alpha));
    return data;
  }


梯度下降

那么梯度下降就可以对梯度函数进行循环即可

  async GradientDescent(
    feature: tf.Tensor<tf.Rank>,
    alpha: number,
    theta: tf.Tensor<tf.Rank>,
    yi: tf.Tensor<tf.Rank>,
    iterations: number,
    lambda: number = 0
  ) {
    let rTheta = theta;
    for (let i = 0; i < iterations; i++) {
      rTheta = await this.Gradient(feature, rTheta, yi, alpha, lambda);
    }
    return rTheta;
  }


线性的逻辑回归

题目大搞意思就是你手上有历史的学生考分和是否能够顺利毕业之间的关系,需要你推导出模型来通过分数预测学生是否能够顺利毕业。


画点阵图

直接后台读出数据,返回给前端即可

  async data1_showData(ctx: Context) {
    const data = ReadData("./data/ex2data/ex2data1.txt");
    ctx.body = data;
  }


如何前端画线就不多说了,画出来的如下图所示

TypeScript版的吴恩达的机器学习课程练习-2_吴恩达

看到上面这个图可以知道,边界线可以用一条直线表示,那么模型可以是:

TypeScript版的吴恩达的机器学习课程练习-2_吴恩达_02

TypeScript版的吴恩达的机器学习课程练习-2_吴恩达_03TypeScript版的吴恩达的机器学习课程练习-2_机器学习_04


计算θ=[0,0]时候的损失值


直接调用代价函数就可以了,具体的θ就传[0,0]即可

  async data1_costValue(ctx: Context) {
    const _ex2Service = new ex2Service();
    const data = ReadData("./data/ex2data/ex2data1.txt");
    const theta = tf.zeros([3, 1]);
    const featureData = tf.tensor(data);
    const feature = featureData.slice([0, 0], [featureData.shape[0], (featureData.shape[1] || 1) - 1]);
    const y = featureData.slice([0, (featureData.shape[1] || 1) - 1], [featureData.shape[0], 1]);
    const cost = await _ex2Service.Cost(feature, theta, y);
    ctx.body = cost;
  }

最终可以看到计算得到的值为: 0.6931471824645996

画边界线

直接调用梯度下降,计算即可拿到下降后的θ

  async data1_GradientDescent(ctx: Context) {
    const _ex2Service = new ex2Service();
    const data = ReadData("./data/ex2data/ex2data1.txt");
    const thInit = tf.zeros([3, 1]);
    const featureData = tf.tensor(data);
    const feature = featureData.slice([0, 0], [featureData.shape[0], (featureData.shape[1] || 1) - 1]);
    const y = featureData.slice([0, (featureData.shape[1] || 1) - 1], [featureData.shape[0], 1]);
    let theta = await _ex2Service.GradientDescent(feature, 0.01, thInit, y, 200000);
    ctx.body = theta.dataSync();
  }


在前端根据返回的θ画出边界线即可

TypeScript版的吴恩达的机器学习课程练习-2_吴恩达_05


非线性的逻辑回归


画点阵图

直接后台读出数据,返回给前端即可

  async data2_showData(ctx: Context) {
    const data = ReadData("./data/ex2data/ex2data2.txt");
    ctx.body = data;
  }


最终画出来的图如下

TypeScript版的吴恩达的机器学习课程练习-2_机器学习_06


我们看到图形可以判断是非线性的边界,而且根据题目的要求需要对自变量x1x2进行转换才行

TypeScript版的吴恩达的机器学习课程练习-2_吴恩达_07

具体的转换方法如下

  async MapFeature(feature: Array<Array<number>>) {
    let data: Array<Array<number>> = [];
    for (let i = 0; i < feature.length; i++) {
      let e: Array<number> = [];
      for (let pow = 1; pow <= 6; pow++) {
        for (let j = 0; j <= pow; j++) {
          e.push(Math.pow(feature[i][0], j) * Math.pow(feature[i][1], pow - j));
        }
      }
      data.push(e);
    }
    return data;
  }


画出lambda=1时候的边界线

代码如下,最终返回边界线的坐标。由于没有找到合适的等高线方法,因此这边调用了我自己写的方法。

async data2_GradientDescent_Lambda1(ctx: Context) {
    const _ex2Service = new ex2Service();
    const data = ReadData("./data/ex2data/ex2data2.txt");
    const dataMap = await _ex2Service.MapFeature(data);
    const thInit = tf.zeros([28, 1]);
    const featureData = tf.tensor(data);
    const feature = tf.tensor(dataMap);
    const y = featureData.slice([0, (featureData.shape[1] || 1) - 1], [featureData.shape[0], 1]);
    let theta = await _ex2Service.GradientDescent(feature, 0.01, thInit, y, 50000, 1);


    var dataFor: Array<Array<number>> = [];
    for (let x1 = -1.2; x1 <= 1.2; x1 += 0.01) {
      for (let x2 = -1.2; x2 <= 1.2; x2 += 0.01) {
        dataFor.push([x1, x2])
      }
    }
    const dataForMap = await _ex2Service.MapFeature(dataFor);
    const dataForH: any = (await _ex2Service.Polynomial(tf.tensor(dataForMap), theta)).dataSync();
    const returnData = Contour(dataForH, -0.01, 0.01, -1.2, 1.2, -1.2, 1.2, 0.01);
    ctx.body = returnData

  }


前端实现方式就不贴代码了,具体的图如下所示

TypeScript版的吴恩达的机器学习课程练习-2_人工智能_08


画出lambda=0时候的边界线

代码如下

lambda=1是一模一样的,只是参数不一样,另外具体的学习速度和梯度下降次数也慢慢试出来的

  async data2_GradientDescent_Lambda0(ctx: Context) {
    const _ex2Service = new ex2Service();
    const data = ReadData("./data/ex2data/ex2data2.txt");
    const dataMap = await _ex2Service.MapFeature(data);
    const thInit = tf.zeros([28, 1]);
    const featureData = tf.tensor(data);
    const feature = tf.tensor(dataMap);
    const y = featureData.slice([0, (featureData.shape[1] || 1) - 1], [featureData.shape[0], 1]);
    let theta = await _ex2Service.GradientDescent(feature,10, thInit, y, 50000, 0);
    //ctx.body = theta.dataSync();

    var dataFor: Array<Array<number>> = [];
    for (let x1 = -1.2; x1 <= 1.2; x1 += 0.01) {
      for (let x2 = -1.2; x2 <= 1.2; x2 += 0.01) {
        dataFor.push([x1, x2])
      }
    }

    const dataForMap = await _ex2Service.MapFeature(dataFor);
    const dataForH: any = (await _ex2Service.Polynomial(tf.tensor(dataForMap), theta)).dataSync();
    const returnData = Contour(dataForH, -0.1, 0.1, -1.2, 1.2, -1.2, 1.2, 0.01);
    ctx.body = returnData

  }


具体的图如下所示


TypeScript版的吴恩达的机器学习课程练习-2_AI开发_09


画出lambda=100时候的边界线

具体就不贴代码了,画出来的图如下所示

TypeScript版的吴恩达的机器学习课程练习-2_AI开发_10


代码下载

将代码上传到了GitHub,想要的小伙伴直接下载吧 点击进入

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月19日 0

暂无评论

推荐阅读
1oBeqNAi2px1