maltadirk 发表于 2022-1-11 20:39

《最优化理论》期末作业2:像素聚类

问题描述

给定一幅高宽为 https://www.zhihu.com/equation?tex=w+%5Ctimes+h 的图像,在其上随机撒上个不同的种子点 https://www.zhihu.com/equation?tex=%5Cbm%7Bp%7D_i 。将图像的每个像素分配给离各自最近的种子点,则这组种子点将图像分割成个区域。在每个区域采用最小二乘法求得最佳的逼近多项式 https://www.zhihu.com/equation?tex=g_i%28x%2C+y%2C+a_i%2C+b_i%29 ,则每个区域的逼近误差为:

https://www.zhihu.com/equation?tex=%5Csum_%7Bx+%5Cin+%5COmega_i%7D%5Csum_%7By+%5Cin+%5COmega_i%7D+%28f%28x%2C+y%29+-+g_i%28x%2C+y%2C+a_i%2C+b_i%29%29%5E2%5Ctag%7B1%7D

个区域(所有像素值)的总误差为:

https://www.zhihu.com/equation?tex=%5Csum_%7Bi+%3D+1%7D%5Em%5Csum_%7Bx+%5Cin+%5COmega_i%7D%5Csum_%7By+%5Cin+%5COmega_i%7D+%28f%28x%2C+y%29+-+g_i%28x%2C+y%2C+a_i%2C+b_i%29%29%5E2%5Ctag%7B2%7D
求:优化种子点位置以获得最优的像素聚类结果 (此时的区域边界和图像特征比较吻合),等价于平均像素值的误差最小。

https://www.zhihu.com/equation?tex=e+%3D+%5Cmin%5Cfrac%7B1%7D%7Bwh%7D%5Csum_%7Bi+%3D+1%7D%5Em%5Csum_%7Bx+%5Cin+%5COmega_i%7D%5Csum_%7By+%5Cin+%5COmega_i%7D+%28f%28x%2C+y%29+-+g_i%28x%2C+y%2C+a_i%2C+b_i%29%29%5E2%5Ctag%7B3%7D
要求:至少采用两种优化算法,每种优化算法测试至少两幅图像,即总共四个结果,自定。
问题分析

为什么要像素聚类?

[*]反证推理:如果不像素聚类,那么仅仅通过一个最小二乘法拟合,几乎很难完成。每个区域都拟合一个最小二乘法,那么难度大大降低。通常,单个区域中像素函数关系也更好表达。这体现了一种分治的思想:分而治之,大问题变小问题,从而可以做得了。
[*]像素的聚类的本质就是划为区域,从而检测出有效的图像边缘。
要点分析:

[*]图像分为灰度图像和伪彩色图像,灰度图像是关于位置坐标的实函数,伪彩色图像是关于位置坐标的向量函数。为了简化求解过程,假定图像为灰度图像,即 https://www.zhihu.com/equation?tex=f%3A+%5Cmathrm%7BR%7D%5E2+%5Cto+%5Cmathrm%7BR%7D 。
[*]图像的像素值通常是离散的无符号整数,且在0~255范围内,通常需要标准化到稠密的实数。又因为使用均方误差,所以标准化到-1~1之间内。
[*]位置坐标是离散的无符号整数,所以在判断属于哪个使用距离时,要标准化到稠密的0~1实数范围内。
[*]https://www.zhihu.com/equation?tex=p_%7Bi%2C+x%7D+%5Cin+%5B0%2C+w%5D , https://www.zhihu.com/equation?tex=p_%7Bi%2C+y%7D+%5Cin+%5B0%2C+h%5D 。在要点3的基础上,也标准化到0~1之间,即 https://www.zhihu.com/equation?tex=p_%7Bi%2C+x%7D+%5Cgets+%5Cfrac%7Bp_%7Bi%2C+x%7D%7D%7Bw%7D%2C+p_%7Bi%2C+y%7D+%5Cgets+%5Cfrac%7Bp_%7Bi%2C+y%7D%7D%7Bh%7D 。
[*]采用非线性最小二乘法。
[*]当越大,所有像素值拟合的效果就越好,但是太大,区域又划分太多,不能分割出有效的图像边缘。通常,一幅图像就几条边缘。因此,的设置也很关键,不能太大,也不能太小。
为了求解方便,我们改写公式(3),则有:

https://www.zhihu.com/equation?tex=e+%3D+%5Cmathop%7B%5Carg+%5Cmin%7D%5Climits_%7B%5Cbm%7BP%7D%2C+%5Cbm%7BA%7D%7D%5Cfrac%7B1%7D%7Bwh%7D%5Csum_%7Bk+%3D+1%7D%5E%7Bwh%7D%28f%28%5Cbm%7Bx%7D%5E%7B%28k%29%7D%29+-+h%28%5Cbm%7Bx%7D%5E%7B%28k%29%7D%2C+%5Cbm%7BP%7D%2C+%5Cbm%7BA%7D%29%29%5E2+%5Ctag%7B4%7D
其中, https://www.zhihu.com/equation?tex=%5Cbm%7Bx%7D%5E%7B%28k%29%7D+%5Cin+%5Cmathrm%7BR%7D%5E2 ,为的矩阵, https://www.zhihu.com/equation?tex=%5Cbm%7BA%7D 为的矩阵。约束条件(已标准化)有:

https://www.zhihu.com/equation?tex=p_%7B1%2C+i%7D+%5Cin+%5B0%2C+1%5D%2C+p_%7B2%2C+i%7D+%5Cin+%5B0%2C+1%5D%5Ctag%7B5%7D
对公式(5)使用拉格朗日乘子法有:

https://www.zhihu.com/equation?tex=%5Csum_%7Bi+%3D+1%7D%5Em+%28s_i+-+r_i%29p_%7B1%2C+i%7D+%2B+%5Csum_%7Bi+%3D+1%7D%5Em%28u_i+-+t_i%29p_%7B2%2C+i%7D+-+%5Csum_%7Bi+%3D+1%7D%5Em%28s_i++%2B+u_i%29+%5Ctag%7B6%7D
构造维向量 https://www.zhihu.com/equation?tex=%5Cbm%7Br%7D%2C+%5Cbm%7Bs%7D%2C+%5Cbm%7Bt%7D%2C+%5Cbm%7Bu%7D ,从而有:

https://www.zhihu.com/equation?tex=%5Cleft%3C%5Cbm%7Bs%7D+-+%5Cbm%7Br%7D%2C+%5Cbm%7Bp%7D_1%5E%5Cmathrm%7BT%7D%5Cright%3E+%2B+%5Cleft%3C%5Cbm%7Bu%7D+-+%5Cbm%7Bt%7D%2C+%5Cbm%7Bp%7D_2%5E%5Cmathrm%7BT%7D%5Cright%3E+-%5Csum%28%5Cbm%7Bs%7D+%2B+%5Cbm%7Bu%7D%29+%5Ctag%7B7%7D
联加公式(4)和(7),从而构造无约束优化:

https://www.zhihu.com/equation?tex=l+%3D+%5Cmathop%7B%5Carg+%5Cmin%7D%5Climits_%7B%5Cbm%7BP%7D%2C+%5Cbm%7BA%7D%2C+%5Cbm%7Br%7D%2C+%5Cbm%7Bs%7D%2C+%5Cbm%7Bt%7D%2C+%5Cbm%7Bu%7D%7D%5Cfrac%7B1%7D%7Bwh%7D%5Csum_%7Bk+%3D+1%7D%5E%7Bwh%7D%28f%28%5Cbm%7Bx%7D%5E%7B%28k%29%7D%29+-+h%28%5Cbm%7Bx%7D%5E%7B%28k%29%7D%2C+%5Cbm%7BP%7D%2C+%5Cbm%7BA%7D%29%29%5E2++%2B+%5Cleft%3C%5Cbm%7Bs%7D+-+%5Cbm%7Br%7D%2C+%5Cbm%7Bp%7D_1%5E%5Cmathrm%7BT%7D%5Cright%3E+%2B+%5Cleft%3C%5Cbm%7Bu%7D+-+%5Cbm%7Bt%7D%2C+%5Cbm%7Bp%7D_2%5E%5Cmathrm%7BT%7D%5Cright%3E+-%5Csum%28%5Cbm%7Bs%7D+%2B+%5Cbm%7Bu%7D%29+%5Ctag%7B8%7D
为了减少优化符号,构造一个 https://www.zhihu.com/equation?tex=4+%5Ctimes+m 的矩阵 https://www.zhihu.com/equation?tex=%5Cbm%7BM%7D :

https://www.zhihu.com/equation?tex=%5Cbm%7Bm%7D_1%5E%5Cmathrm%7BT%7D+%3D+%5Cbm%7Bs%7D%2C+%5Cbm%7Bm%7D_2%5E%5Cmathrm%7BT%7D+%3D+%5Cbm%7Br%7D%2C+%5Cbm%7Bm%7D_3%5E%5Cmathrm%7BT%7D+%3D+%5Cbm%7Bu%7D%2C+%5Cbm%7Bm%7D_4%5E%5Cmathrm%7BT%7D+%3D+%5Cbm%7Bt%7D%5Ctag%7B9%7D
问题解决

接下来,只需优化无约束公式(9)了,可以采用两种方法:同时优化和交替优化(先固定,后优化;再固定,后优化;重复)。本文采用同时优化。
实验

代码实现要点:

[*]公式(9)中的函数 https://www.zhihu.com/equation?tex=h 是很难写成解析表达式的,所以基于中心差分求梯度的数值解。基于中心差分求梯度对预设置的微小变量很敏感。简接验证了误差方向传播算法的优越性。
[*]可视化,绘制出任意相邻区域 https://www.zhihu.com/equation?tex=%5COmega_i+%5Ccap++%5COmega_j 中像素点,即图像边缘。像素聚类的本质就是所有像素都拟合得很好,所以拟合的函数 https://www.zhihu.com/equation?tex=+h%28%5Cbm%7Bx%7D%2C+%5Cbm%7BP%7D%2C+%5Cbm%7BA%7D%29 的输出分布相似于 https://www.zhihu.com/equation?tex=f%28%5Cbm%7Bx%7D%29 的分布,可以近似说明像素聚类的效果好,因此采用比较两者的分布图像即可,简化可视化的难度。题目要求的可视化就留给读者自己了。
[*]在要点2的基础上,就可以随机初始化一幅较小的图像,减少训练时间。
[*]拟合函数需要对输出的像素值进行裁剪修正。
[*]只能通过优化算法来改变待训练参数的值,其他地方只能使用待训练参数。
代码:
#! -*- coding:utf-8 -*-

'''
@Author:      ZM
@Date and Time: 2021/12/17 18:07
@File:          pixel_clustering.py
'''

import numpy as np
import matplotlib.pyplot as plt

def loss_fun(arg_X, arg_P, arg_A, arg_M):
    '''
    :param arg_X: 像素矩阵
    :return: 无约束优化
    '''

    '''平均均方误差
    '''
    h, w = arg_X.shape
    avg_errors = 0.
    for i in range(w):
      for j in range(h):
            ds = np.sum(np.square(arg_P - np.array(, dtype=arg_P.dtype)[:, None]), axis=0)    # 求距离,不开平方
            min_indexs = np.where(ds == np.min(ds))      # 所有最短距离的索引
            ls_pixel = 0
            for min_index in min_indexs:
                ls_pixel += (arg_A - i / w) + (arg_A - j / h) ** 2    # 非线性最小二乘法拟合的像素值
            ls_pixel /= min_indexs.size
            avg_errors += (arg_X - ls_pixel) ** 2   # 均方误差
    avg_errors /= arg_X.size    # 平均均方误差

    ''' 拉格朗日乘子项
    '''
    Lagrange = (np.einsum('i, i', arg_M - arg_M, arg_P) +
                np.einsum('i, i', arg_M - arg_M, arg_P) -
                np.sum(arg_M))

    return 0.999 * avg_errors + 0.001 * Lagrange   # 无约束,系数分配让所有像素值拟合的效果更好一点

def grad_fun(arg_X, arg_P, arg_A, arg_M, f, epsilon=1e-14): # 基于中心差分求梯度对epsilon敏感
    '''
    :param arg_X: 像素矩阵
    基于中心差分求梯度
    '''

    grad_P = np.zeros_like(arg_P)
    grad_A = np.zeros_like(arg_A)
    grad_M = np.zeros_like(arg_M)

    for i in range(arg_P.shape):
      for j in range(arg_P.shape):
            t = arg_P
            arg_P = t + epsilon
            e1 = f(arg_X, arg_P, arg_A, arg_M)
            arg_P = t - epsilon
            e2 = f(arg_X, arg_P, arg_A, arg_M)
            grad_P = (e1 - e2) / (2 * epsilon)
            arg_P = t

    for i in range(arg_A.shape):
      for j in range(arg_A.shape):
            t = arg_A
            arg_A = t + epsilon
            e1 = f(arg_X, arg_P, arg_A, arg_M)
            arg_A = t - epsilon
            e2 = f(arg_X, arg_P, arg_A, arg_M)
            grad_A = (e1 - e2) / (2 * epsilon)
            arg_A = t

    for i in range(arg_M.shape):
      for j in range(arg_M.shape):
            t = arg_M
            arg_M = t + epsilon
            e1 = f(arg_X, arg_P, arg_M, arg_M)
            arg_M = t - epsilon
            e2 = f(arg_X, arg_P, arg_M, arg_M)
            grad_M = (e1 - e2) / (2 * epsilon)
            arg_M = t

    return grad_P, grad_A, grad_M

def fitting_fun(i, j, arg_P, arg_A):
    '''
    :param i: 横坐标
    :param j: 纵坐标
    :return: 像素值
    '''
    ds = np.sum(np.square(arg_P - np.array(, dtype=arg_P.dtype)[:, None]), axis=0)# 求距离,不开平方
    min_indexs = np.where(ds == np.min(ds))# 所有最短距离的索引
    ls_pixel = 0
    for min_index in min_indexs:
      ls_pixel += (arg_A - i / w) + (arg_A - j / h) ** 2# 非线性最小二乘法拟合的像素值
    ls_pixel /= min_indexs.size
    return max(min(ls_pixel, 1), 0)   # 裁剪修正

h, w = 10, 10
X = np.random.uniform(size=(h, w)) + np.random.random(size=(h, w)) + np.random.normal(size=(h, w))# 随机初始化一幅图像
X = 2 * (np.max(X) - X) / (np.max(X) - np.min(X)) - 1   # 将像素值标准化到-1~1范围内
m = int(np.sqrt(h * w))   # 种子点的个数,既不能太大,也不能太小

'''待优化的参数
'''
Px = np.random.uniform(size=(1, m))   # 标准化到0~1范围内
Py = np.random.uniform(size=(1, m))   # 标准化到0~1范围内
P = np.concatenate(, axis=0)
A = np.random.uniform(low=-np.sqrt(6 / m), high=np.sqrt(6 / m), size=(2, m))
M = np.random.uniform(low=-np.sqrt(6 / m), high=np.sqrt(6 / m), size=(4, m))

''' P的初始输入
'''
s = * w:.2f}, {P * h:.2f}\n' for i in range(m)]
s[-1] = s[-1].strip()
with open('logs_init.txt', 'w', encoding='utf-8') as f:
    f.writelines(s)

total_steps = 5000   # 总迭代次数
lr = init_lr = 0.01    # 学习步长
min_loss = 1e32

'''采用牛顿加速梯度下降法
'''
vP, vA, vM = np.zeros_like(P), np.zeros_like(A), np.zeros_like(M)
momentum = 0.9
for step in range(total_steps):
    '''只能通过优化算法来改变带训练参数的值,其他地方只能使用待训练参数
    '''
    grad_P, grad_A, grad_M = grad_fun(X, P, A, M, loss_fun)
    vP = momentum * vP + grad_P
    vA = momentum * vA + grad_A
    vM = momentum * vM + grad_M
    P -= lr * vP
    A -= lr * vA
    M -= lr * vM

    tmp_val = loss_fun(X, P, A, M)
    print(f'{lr=}, {tmp_val=}')   # 输出每一步的训练日志

    if min_loss > tmp_val:
      min_loss = tmp_val

    if step < 2000:
      lr = init_lr
    elif step < 3000:
      lr = init_lr * 0.1
    elif step < 4000:
      lr = init_lr * 0.01
    else:
      lr = init_lr * 0.001

''' P的输出
'''
P = np.clip(P, 0, 1)    # 裁剪修正
s = * w:.2f}, {P * h:.2f}\n' for i in range(m)]
s[-1] = s[-1].strip()
with open('logs_clip.txt', 'w', encoding='utf-8') as f:
    f.writelines(s)

'''可视化
'''
fitting_X = np.empty_like(X)
for i in range(w):
    for j in range(h):
      fitting_X = fitting_fun(i, j, P, A)

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(X.flatten(), 'o', label='Real fun')
ax.plot(fitting_X.flatten(), 'd', label='Fitting fun')
for i in range(m):
    ax.plot(np.array( * P] * 1000), np.random.uniform(size=1000))
ax.legend()
fig.savefig('rst.png')
plt.close()图1表明像素值拟合的效果还是挺好的。



图1 使用拉格朗日约束:像素值分布比较。

使用拉格朗日约束:约束条件的训练结果并没有出现过多,表明拉格朗日乘子项起到了约束作用。
的初始输入:
3.46, 4.82
2.04, 7.73
2.25, 4.06
6.91, 9.51
4.87, 5.49
6.86, 7.10
0.74, 9.39
3.15, 0.59
2.14, 7.19
4.83, 3.18
的输出:
4.18, 7.01
0.00, 2.76
6.92, 2.00
3.66, 3.72
8.52, 6.82
3.85, 2.71
2.23, 4.81
5.88, 2.92
8.57, 0.00
2.55, 10.00由于拉格朗日乘子法引入过多参数,本文还尝试了只用梯度裁剪来防止优化的超出的可行解。而且,公式(9)是公式(5)的必要非充分条件,所以为了防止优化的 超出 的可行解,还可以组合拉格朗日乘子法和梯度裁剪。
图2表明梯度裁剪的拟合效果更好一点。



图2 使用梯度裁剪:像素值分布比较。

使用梯度裁剪:约束条件的训练结果也没有出现过多,表明梯度裁剪也起到了约束作用。
的初始输入:
4.63, 9.90
6.64, 9.96
9.56, 0.89
2.52, 2.07
2.11, 7.17
4.05, 7.90
0.34, 1.28
4.03, 5.24
7.34, 8.28
4.85, 7.07
的输出:
4.63, 9.90
6.64, 9.96
9.56, 0.89
2.52, 2.07
2.11, 7.17
4.05, 7.90
0.34, 1.28
4.03, 5.24
7.34, 8.28
4.85, 7.07组合拉格朗日乘子法和梯度裁剪。


的输入:
4.49, 1.98
5.70, 8.08
9.49, 3.07
3.08, 1.39
7.77, 4.90
8.09, 2.79
8.50, 0.83
7.24, 8.99
7.86, 3.21
4.20, 1.82
的输出:
4.41, 0.00
6.72, 9.50
9.63, 3.13
3.74, 1.19
7.37, 7.12
10.00, 4.56
6.76, 0.00
8.05, 8.97
8.16, 0.35
5.20, 1.55
页: [1]
查看完整版本: 《最优化理论》期末作业2:像素聚类