Lang's Blog

Keep writing, Keep loving

注:本篇文章因转载次数过多,我找不到源头 ORZ

1.梯度下降相关概念

  • 假设函数(hypothesis function)

    所谓假设函数,就是监督学习中,我们拟合样本特征映射到目标变量的函数,记为$h _ θ(x)$,θ为函数的参数。例如,一个简单的线性回归拟合函数可以表示为:

$$
h_θ(x) = θ_0 + θ_1·x
$$

  • 损失函数 loss function

    又称为代价函数。通常用损失函数来度量拟合的程度,从而评估模型拟合的好坏,记为*J(θ)*。注意,损失函数是关于 θ 的函数!也就是说,对于损失函数来讲,θ不再是函数的参数,而是损失函数的自变量!例如,对于线性回归的损失函数可以表示为:

$$
J(θ)=1/(2n) * \sum(h_θ (x_i)−y_i)^2
$$
  其中n表示样本数量,$x_i$

  • 损失函数的梯度:

  损失函数的梯度即对$θ _ i$求偏导,由于损失函数是关于 θ 的函数,因此,θ 的取值不同,得出来的的梯度向量也是不同的。借用“下山”的比喻来解释,θ 的不同取值,相当于处于山上的不同位置,每一个位置都会计算出一个梯度向量 ▽J(θ)

2.梯度下降过程

**(1) 学习得到损失函数J(θ)及样本点$x^i$的损失:

  例如,对于线性回归模型的假设函数为:$h _ θ(x) = θ _ 0+θ _ 1·x_1+θ _ 2·x _ 2$,则损失函数为:$J(θ)=1/(2n) * \sum(θ _ 0+θ _ 1·x _ 1+θ _ 2·x_2-y)^2$;我们为样本添加一个维度$x_0$,$x_0$ 恒等于 1。则,我们可以变损失函数表示为:$J(θ)=1/(2n) * \sum(θ _ 0·x_0+θ _ 1·x_1+θ _ 2·x_2-y)^2$
  为了便于讲解和理解,我们先只取一个样本点进行计算。对于样本点$x^1=(x_0=1, x_1=1, x_2=2)$,对应的目标变量$y^1 =10$,的损失为:$J(θ)^1=1/2 * (θ _ 0+θ _ 1+2 * θ _ 2-10)^2$

(2) 求出样本点$x^i$损失函数的梯度向量:

  根据*J(θ)*,求出模型损失函数的梯度向量表示为 :
$$
▽J(θ)=<(θ_0·x_0+θ_1·x_1+θ_2·x_2-y) * x_0, (θ_0·x_0+θ_1·x_1+θ_2·x_2-y) * x_1, (θ_0·x_0+θ_1·x_1+θ_2·x_2-y) * x_2>
$$
  根据$J(θ)^1$,求出样本点$x^1$ 对应的梯度$▽J(θ)^1=<(θ _ 0+θ _ 1+2 * θ _ 2-10), (θ _ 0+θ _ 1+2 * θ _ 2-10), (θ _ 0+θ _ 1+2 * θ _ 2-10) * 2>$

(3) 初始化假设函数的参数 θ ,得到相应的梯度向量:

  对 θ 进行随机取值,假设$θ _ i$第一次全部取0,$θ_0=<0,0,0>$;
  将$θ^0$带入$J(θ)^1$,得到 取$θ^0$时的损失为 $J(θ)_0^1=1/2 * (0+0+2 * 0-10)^2=50$
  将$θ^0$带入$▽J(θ)^1$,得到$θ^0$处的梯度向量为$▽J(θ)_0^1=<-10,-10,-20>$ ;   

(4) 根据梯度下降步长,进行梯度下降迭代:

  设立步长α=0.1,对$θ^0$进行梯度下降,得到$θ^1$
  第一次梯度下降:

$$
θ^1= θ^0-α*▽J(θ) _ 0^1 = <0,0,0>-0.1 * <-10,-10,-20> = <1,1,2>
$$

  将$θ^1$带入$J(θ)^1$ ,得到 取$θ^0$ 时的损失为$J(θ) _ 0^1=1/2 * (1+1+2 * 2-10)^2=8$
  将$θ^1$带入$▽J(θ)^1$,得到$θ^0$处的梯度向量为$▽J(θ) _ 0^1= < -4,-4,-8 >$;
  第二次梯度下降:
  
$$
θ _ 2=θ^1-α*▽J(θ) _ 0^1=<1,1,2>-0.1 * <-4,-4,-8>=<0.4,0.4,1.2>
$$

  将$θ^2$带入$J(θ)^1$,得到 取$θ^2$时的损失为$J(θ) _ 0^1=1/2 * (0.4+0.4+2 * 1.2-10)^2=23.12$
  此时我们发现,$θ^2$处的损失为23.12,大于$θ^1$处的损失8,说明,我们可能步子迈的大了,跨过了最低点,我们重新设定α = 0.05,重复上述过程:
  重新设立步长 α = 0.05
  第一次梯度下降:
  
$$
θ_1 = θ0 - α * ▽J(θ)01 = < 0,0,0 > - 0.05 * < -10,-10,-20 > = < 0.5,0.5,1 >
$$

  将$θ^1$带入$J(θ)^1$ ,得到 取$θ^0$时的损失为 $J(θ) _ 0^1=1/2 * (0.5+0.5+2 * 1-10)^2=24.5$
  将$θ^1$带入$▽J(θ)^1$ ,得到$θ^0$处的梯度向量为$▽J(θ) _ 0^1=<-7,-7,-14>$;
  第二次梯度下降:
  
$$
θ_2 = θ1 - α * ▽J(θ)01 = < 1,1,2 > - 0.05 * < -7,-7,-14 > = < 1.35,1.35,2.7 >
$$

  将$θ^2$带入$J(θ)^1$,得到 取$θ^2$时的损失为$J(θ) _ 0^1=1/2 * (1.35+1.35+2 * 2.7-10)^2=1.805$
  将$θ^2$ 带入$▽J(θ)^1$,得到$θ^2$处的梯度向量为$▽J(θ) _ 0^1=<-1.9,-1.9,-3.8>$

  此时可以继续进行第三次、第四次。。。第n次,那么什么时候梯度下降停止?

  我们可以设置一个梯度下降算法终止距离值 ε,即梯度下降的距离都小于 ε 时,算法终止。假设我们设置 ε = 0.1(实际运用中,一般会设定一个更小的值,例如$1e^{-5}$),上述$θ^2$的梯度向量为< -1.9,-1.9,-3.8 >,梯度向量中的3个值的绝对值都大于ε ,因此继续进行梯度下降,假设经过n次迭代后的梯度向量为<-0.04,-0.04,-0.08>,三个值的绝对值都小于0.1,则算法终止。

  通过上述过程可以看出,重新设置步长α后,第二次梯度下降过程没有跨过损失函数最低点,而是更加接近最低点。不断的梯度下降,使得损失函数损失在不断的变小:$θ^0$的损失为50,$θ^1$的损失为24.5,$θ^2$的损失为1.805。 初始在本文的例子中,由于是我随便写的一个损失函数,所以经过2-3次梯度下降之后就接近了损失函数最低点,实际运用中当然不可能这么轻松,梯度下降可能要经过n次迭代,才能接近损失函数的极小值点。另外,梯度下降过程中,除了损失在不断减小,梯度向量也在不断变小:$θ^0$的梯度为< -10,-10,-20 > ,$θ^1$的梯度为< -7,-7,-14 >,$θ^2$的梯度为< -1.9,-1.9,-3.8 >。梯度越来越小,说明“山坡”越来越平缓,不像一开始那么”陡峭“,那么可以说明,我们已经接近”山谷“了。实际上,随着不断的梯度下降,梯度向量中的每个值会越来月接近于0。

  讲到这里,大家不要忘记一个事实:上述过程中,我们只选取了一个样本点$x^1$进行梯度下降。这种随机选取一个样本点进行梯度下降,找到最优参数θ的方法,就叫做随机梯度下降。而如果我们利用所有的样本数据进行梯度下降,这种方法就叫做批量梯度下降。毫无疑问,批量梯度下降要消耗更多的计算内存和训练时间;而随即梯度下降虽然训练速度快,但是可能得到只是局部最优解。我们也可以折中一下,一共有n个样本,我们取m个子样本进行梯度下降(1<m<n),既提高训练速度,又在一定程度上避免算法收敛到局部最优,这种方法就叫做小批量梯度下降

问题描述

采用JSR303规范的validation库进行后端验证,但是使用BindingResult对象后却获取不到错误信息
局部代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;

@TableId
private Long brandId;

@NotEmpty
@NotBlank(message = "品牌名不能为空")
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach((item)->{
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
return R.error(400, "提交数据不合法").put("data", map);
} else {
brandService.save(brand);
return R.ok();
}
}

解决方案

仔细排除后发现实际代码并没有什么问题,debug后也发现result对象中的errors字段中并没有获取到错误对象,最后通过修改pom文件中的依赖解决了这种问题,在pom中应包含有如下两个依赖,缺一不可,我就是漏了hibernate-validator导致的错误

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.0.Alpha3</version>
</dependency>

问题描述

工程编译报错
java:找不到符号

问题抽象

定位错误可以发现是getter setter的问题
由于采用了@Data注解,所以优先考虑是lombok的问题

解决方案

首先排除以下可能

lombok插件未安装
设置中注解处理未打开

那么剩下的问题就是pom.xml文件中的版本设置里,调整为以下

1
2
3
4
5
6
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

完美解决!

maven修改国内镜像源,提升速度,以下面的阿里镜像为例

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

修改maven镜像需修改settings.xml文件,路径如下:

apache-maven-x.x.x\conf\settings.xml

定位到标签,修改内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<mirrors>
<!-- 阿里云仓库 -->
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

<!-- 中央仓库1 -->
<mirror>
<id>repo1</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://repo1.maven.org/maven2/</url>
</mirror>

<!-- 中央仓库2 -->
<mirror>
<id>repo2</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://repo2.maven.org/maven2/</url>
</mirror>
</mirrors>

拉取镜像
docker pull mysql:5.7

启动mysql,并将部分配置文件映射到宿主机、

docker run -p 3306:3306 --name mysql -v /mydata/mysql/log:/var/log/mysql -v /mydata/mysql/data:/var/lib/mysql -v /mydata/mysql/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
配置远程账号连接
先进入bashdocker exec -it mysql /bin/bash
进入mysql command mysql -u root -p

若密码错误,添加跳过认证的配置重设密码 不断输入exit回车退出bash状态
vim /mydata/mysql/conf/my.cnf 追加 skip-grant-tables具体如下

1
2
3
4
5
6
7
8
9
10
11
12
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
skip-grant-tables

重新进入docker里的mysql command重设密码
use mysql; 选择数据库
update user set authentication_string=PASSWORD("root") where user='root'; 重设密码
flush privileges; 刷新权限

重启mysql容器,docker restart mysql
收工!

遇到这样的问题,我觉既不是没有设置“更新类和资源”,也不是啥插件问题,更和自动编译没啥关系
而是没有以debug模式运行!!
以run模式运行的话热部署是不起作用的。

经常在windows中部署相关服务,但是偶尔会存在端口占用导致服务启动失败的问题,可以用简单的两个命令解决该问题

1.查询占用

右键开始菜单中命令行程序(cmd),以管理员身份运行,输入以下命令
netstat -aon|findstr 8080

2.终结进程

将以上查询到的信息中的PID(每行的最后一个字段)填入以下命令中执行
taskkill /f /pid 2432

0%