朴素梯度下降
前言
对于求局部最优解的情况,可采用梯度下降逼近最小值。这也是机器学习最基本的东西。
一、服务中心的最佳位置
二、题解
梯度下降,讲究数据选择的随机性/批量度,学习率,衰减率,误差精度。
// 小批量梯度下降。
func getMinDistSum(positions [][]int) float64 {
n := len(positions)
// 设置批量数
batchSize = n
// 初始化起点值
var x,y float64
for _,p := range positions {
x += float64(p[0])
y += float64(p[1])
}
x /= float64(n)
y /= float64(n)
// 开始小批量下降
for {
xPre,yPre := x,y
shuffle(positions)// 每轮都洗一次数据
for i := 0;i < n;i += batchSize {
j := min(n,i + batchSize)
// 批误差求和
var dx,dy float64
for k := i;k < j;k++ {
curX,curY := float64(positions[k][0]),float64(positions[k][1])
// 注意分母为0,+eps
dx += (x - curX) / (math.Sqrt((x - curX) * (x - curX) + (y - curY) * (y - curY)) + eps)
dy += (y - curY) / (math.Sqrt((x - curX) * (x - curX) + (y - curY) * (y - curY)) + eps)
}
// 更新x,y
x -= alpha * dx
y -= alpha * dy
// 每批次,学习率衰减。
alpha *= (1.0 - decay)
}
// 如果更新前后差值在误差内,就不用迭代了。
gap := math.Sqrt((x - xPre) * (x - xPre) + (y - yPre) * (y - yPre))
if eps > gap {
break
}
}
return getDistance(positions,x,y)
}
// 保住批量数据取数据。
func shuffle(positions [][]int) {
n := len(positions)
for i := 0;i < n;i++ {
x,y := positions[i][0],positions[i][1]
idx := rand.Intn(n)
positions[i][0],positions[i][1] = positions[idx][0],positions[idx][1]
positions[idx][0],positions[idx][1] = x,y
}
}
// 设置学习率/最小误差/防止来回波动的衰减值
var (
alpha float64 = 1
eps float64 = 1e-7
decay float64 = 1e-3
batchSize int
)
// 得到服务中心到所有点的距离
func getDistance(positions [][]int,x,y float64) float64 {
var rs float64
for _,p := range positions {
i,j := float64(p[0]),float64(p[1])
rs += math.Sqrt((i - x) * (i - x) + (j - y) * (j - y))
}
return rs
}
// 取最小值
func min(x,y int) int {
if x < y {
return x
}
return y
}
总结
1)朴素梯度下降,抓住几个参数就OK了,学习率,衰减值,误差精度,批量大小,再注意数据的随机选择即可。