多变量优化 - scipy.optimize 输入解析错误

Multivariate Optimization - scipy.optimize input parsing error

我将上面的 rgb 图片保存为 tux.jpg。现在我想得到这个图像的最接近的近似值,它是两个向量的外积,即形式为 A·BT.

这是我的代码 -

#load image to memory
import Image
im = Image.open('tux.jpg','r')
#save image to numpy array
import numpy as np
mat = np.asfarray(im.convert(mode='L')) # mat is a numpy array of dimension 354*300
msizex,msizey = mat.shape
x0 = np.sum(mat,axis=1)/msizex
y0 = np.sum(mat,axis=0)/msizey

X0 = np.concatenate((x0,y0)) # X0.shape is (654,)
# define error of outer product with respect to original image
def sumsquares(X):
    """ sum of squares - 
    calculates the difference between original and outer product

    input X is a 1D numpy array with the first 354 elements
    representing vector A and the rest 300 representing vector B.
    The error is obtained by subtracting the trial  $A\cdot B^T$ 
    from the original and then adding the square of all entries in 
    the matrix.
    """
    assert X.shape[0] == msizex+msizey
    x = X0[:msizex]
    y = X0[msizex:]
    return np.sum(
        (
        np.outer(x,y) - mat
        )**2
    )
#import minimize
from scipy.optimize import minimize
res = minimize(sumsquares, X0,
               method='nelder-mead',
               options={'disp':True}
)
xout = res.x[:msizex]
yout = res.x[msizex:]
mout = np.outer(xout,yout)
imout= Image.fromarray(mout,mode='L')
imout.show()

结果是

     Optimization terminated successfully.
     Current function value: 158667093349733.531250
     Iterations: 19
     Function evaluations: 12463

我觉得这不够好。有什么办法可以改善吗?输出中的噪声甚至与原始图片中的结构长度不同。我的猜测是该算法没有通过。我该如何调试或改进它?

EDIT1:我用代码

创建了下面的图像
size = 256
mat0 = np.zeros((size,size))
mat0[size/4:3*size/4,size/4:3*size/4] = 1000
#mat0[size/4:3*size/4,] = 1000
#mat0[:3*size/4,size/4:] = 1000

im0 = Image.fromarray(mat0)
im0.show() 

这两行注释掉的结果是另外两张图片。这是我的实验结果 -

  1. 正方形在中间。 输入 -
    输出 - 相同
  2. 乐队在中间。 输入 -
    输出 -
  3. 东北方的白色大块 输入 -
    输出-

虽然这比我预期的要好得多,但案例 2 和案例 3 最终仍然是错误的。我希望 minimize 函数的参数是我认为的意思。

1) 第一张图的渲染问题貌似是numpy数组转图的问题。我通过 运行:

获得了正确的渲染
imout = Image.fromarray(mout/np.max(mout)*255)

(即将图像归一化为最大值255,让它自动判断模式)。

一般来说,要检查 Image.fromarray 是否正常工作,将 imout.show() 的输出与

进行比较很有用
import matplotlib.pyplot as plt
plt.matshow(mout/np.max(mout)*255, cmap=plt.cm.gray)

你应该得到相同的结果。顺便说一句,通过这样做,我得到了所有其他 3 个案例。

2) 其次,tux.png的主要问题是,仅用两个一维向量的外积是不可能重建具有如此详细结构的图像

(这往往适用于简单的图像,例如上面显示的块状图像,但不适用于具有少量对称性和许多细节的图像)。

为了证明这一点:

  • 存在矩阵分解技术,可以将矩阵重构为两个低秩矩阵 M=AB 的乘积,例如 sklearn.decomposition.NMF

  • 在这种情况下,将 A 和 B 的等级设置为 1 等同于您的问题(使用不同的优化技术)。

  • 使用下面的代码,您可以很容易地看到 n_components=1(相当于两个一维向量的外积),得到的重构矩阵看起来非常和你的方法输出的差不多,n_components越大重建越好。

为了可重复性:

import matplotlib.pyplot as plt
from sklearn.decomposition import NMF

nmf = NMF(n_components=20)
prj = nmf.fit_transform(mat)

out = prj.dot(nmf.components_)
out = np.asarray(out, dtype=float)

imout = Image.fromarray(out)
imout.show()

为了说明,这是具有 1 个分量的 NMF 重建(这恰好是两个一维向量之间的外积):

有 2 个组件:

这是具有 20 个分量的 NMF 重建。

这清楚地表明单个一维外积对于此图像是不够的。但是它适用于块状图像。

如果您不局限于向量的外积,则可以选择矩阵分解。顺便说一句,存在大量的矩阵分解技术。 sklearn 中的另一种选择是 SVD

3) 最后,x0 和 y0 可能存在缩放问题。请注意,np.outer(x0, y0) 的元素比 mat 的元素高几个数量级。

虽然我仍然使用提供的代码在 3 个块状示例中获得了不错的结果,但一般来说,在计算平方差时具有可比较的比例是一个很好的做法。例如,您可能希望缩放 x0 和 y0,以便 np.outer 的范数与 mat 的范数相当。