求以矩阵为输入的函数的最小值

问题描述

给定一个形状为 (64,64)(= 图像)的 numpy 数组和一个将该图像作为输入的任意函数,我想找到最小化函数的图像。假设函数计算对比度。

示例:

import numpy as np

def contrast(X):
    vmin,vmax = int(np.min(X)),int(np.max(X))
    num = vmax - vmin
    denom = vmax + vmin
    if denom == 0:
        return 0
    else:
        return num / denom

img = np.random.randint(256,size=(64,64),dtype=np.uint8)
res = contrast(img)

Scipy 提供了 fmin(),但该函数不适用于如此大的输入。任何想法如何找到 使函数最小化的图像?

解决方法

Run the code in google colab

它绝不是完美的,但您至少可以通过简单的梯度下降优化和自动微分来接近局部最小值¹,例如autograd。为了使自动渐变起作用,您很可能必须将图像数据转换为浮点数,进行优化,然后转换并转换回整数。原则上,这可能会导致您错过最小值或找到错误的最小值或陷入局部最小值。

1:请注意,这绝不保证您在任何情况下都能找到全局最小值,但会找到一个最小值。

import autograd.numpy as np
from autograd import elementwise_grad

def michelson_contrast(image):
    vmin,vmax = np.min(image),np.max(image)
    if (vmax + vmin) > 1e-15:
        return (vmax - vmin) / (vmax + vmin)
    return 0

对于您指定的特定函数,Michelson contrast,优化收敛极其缓慢,

f = michelson_contrast
df = elementwise_grad(f)
img = np.random.randint(256,size=(100,100)).astype(np.float64)

# Simple gradient descent.
for i in range(1,(max_iterations := 100000) + 1):
    img -= 10**3 * df(img)

# Round and cast the image back to integer values.
img = np.round(img).astype(int)

但是一个 100 x 100 随机测试在大约一分钟内收敛到我的笔记本电脑上。

           iter.             function
--------------------------------------
               0         1.0000000000
           10000         0.6198908490
           20000         0.4906918649
           30000         0.3968742592
           40000         0.3204002330
           50000         0.2539835041
           60000         0.1942016682
           70000         0.1386916909
           80000         0.0863448569
           90000         0.0361678029
          100000         0.0003124169

四舍五入为整数,答案是 f = 0 的精确最小值,但当然存在很多(准确地说是 256 个):

[[146 146 146 ... 146 146 146]
 [146 146 146 ... 146 146 146]
 [146 146 146 ... 146 146 146]
 ...
 [146 146 146 ... 146 146 146]
 [146 146 146 ... 146 146 146]
 [146 146 146 ... 146 146 146]]

另一个例子,RMS contrast,收敛得更快(不到一秒)

def rms_contrast(image):
    N = image.size
    image_mean = np.mean(image)
    return np.sum((image - image_mean)**2) / N

f = rms_contrast
df = elementwise_grad(f)
img = np.random.randint(256,100)).astype(np.float64)

for i in range(1,(max_iterations := 100) + 1):
    img -= 10**3 * df(img)

img = np.round(img).astype(int)

           iter.             function
--------------------------------------
               0      5486.3646543900
              10        63.2534779216
              20         0.7292629494
              30         0.0084078294
              40         0.0000969357
              50         0.0000011176
              60         0.0000000129
              70         0.0000000001
              80         0.0000000000
              90         0.0000000000
             100         0.0000000000

以及生成的图像(在转换回整数后又是一个完美的最小值)。

[[126 126 126 ... 126 126 126]
 [126 126 126 ... 126 126 126]
 [126 126 126 ... 126 126 126]
 ...
 [126 126 126 ... 126 126 126]
 [126 126 126 ... 126 126 126]
 [126 126 126 ... 126 126 126]]

除非函数非常复杂或计算量很大,或者输入图像很大,否则这种方法至少应该让你更接近你的答案。