使用神經網路生成抽象隨機藝術

dicksonjyl560101發表於2019-02-15

https://www.toutiao.com/a6657911844235117059/


使用神經網路生成抽象隨機藝術

隨著深度神經網路作為分類或迴歸模型變得非常流行,這些模型也可以用作生成模型來建立人工資料,例如建立新影像甚至文字資料。

下面是使用抽象藝術生成演算法完成的生成的影像:

使用神經網路生成抽象隨機藝術

生成的RGB影像(512x512)

這個實現是在python NumPy中實現的,並解釋了影像生成的工作方式。

隨機訓練神經網路時,初始化權重矩陣“W i ,i=1,....,n l +1”,其中n l 為神經網路的隱藏層數。

在這個實現中,權重將通過繪製標準正態分佈 N(μ=0,σ2=1) 來初始化,如下面的程式碼所示:


import seaborn as sns

import warnings
warnings.filterwarnings("ignore")
import argparse
import sys
import os
import numpy as np
import matplotlib 
import matplotlib.pyplot as plt
import time
### Draw a large vector of random numbers by standard normal distribution
random_numbers = np.random.normal(loc=0.0, scale=1.0, size=2000)
### Now suppose we wanted to approximate the density function of those random numbers, we suppose
### the kernel density estimate to look like the standard normal distribution
sns.set_style("whitegrid")
sns.kdeplot(random_numbers, bw=0.5)
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

<matplotlib.axes._subplots.AxesSubplot at 0x9a66550>

使用神經網路生成抽象隨機藝術

隨機藝術演算法是如何工作的

神經網路主要用作分類或迴歸模型。

假設我們有一些輸入資料X i ,想要預測結果Y i ,其中X i 是維度p的向量,Y i 是維度k的向量。

在這種情況下,我們有一個預測可能的類k的分類問題。

對於生成的藝術,取決於我們想要選擇的顏色模式(RGB、灰度、CMYK和HSL(HSV)),在輸出層中我們有3個、1個、4個或3個輸出神經元。

由於(對於float資料型別)這些 “ colour-values ”(顏色值) 的範圍是[0,1],因此每個輸出神經元的 “ weighted_input ”(加權輸入) 將被轉換為一個sigmoid啟用函式,如下所示:


x = np.arange(-10, 10, 0.1)

sigmoid = 1 / (1 + np.exp(-x))
plt.plot(x, sigmoid)
plt.xlabel("weighted_input")
plt.ylabel("color-value")
plt.show()
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

對於隱藏層啟用,可以選擇不同的啟用函式,但是由於我們通過sigmoid對映weighted_input並希望colour-values大約在0.25-0.75範圍之間建立。因此對於每個隱藏層,tanh啟用將如下所示。注意,這個實現在預設情況下選擇預設啟用函式。在後面我寫了一個NeuralNet類,它可以通過輸入引數列表layers_dims和activations_fnc來選擇不同的隱藏層神經元和層啟用(但對於輸出層,請確保啟用函式對映到範圍[0,1])。


x = np.arange(-10, 10, 0.1)

tanh = np.tanh(x)
plt.plot(x, tanh)
plt.xlabel("x")
plt.ylabel("tanh(x)")
plt.show()
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

該演算法還組合了alpha通道,alpha通道是一個8位的灰度通道,該通道記錄影像中的透明度資訊,定義為透明、不透明和半透明 。當一種顏色(源圖)與另一種顏色(背景圖)混合時,例如,當一幅影像被疊加到另一幅影像上時,源圖顏色的alpha值用於確定最終的顏色。如果alpha值不透明,源圖顏色將覆蓋目標顏色;如果是透明的,源圖顏色是不可見的,顯示背景圖顏色。如果alpha值介於兩者之間,則生成的顏色具有不同程度的前後背景混合顏色,從而建立半透明效果。

輸出層中的顏色值將通過計算輸入資料的前向傳遞來生成。因此,很容易實現一個NeuralNet類及其所需的方法,在這種情況下:


class NeuralNet:

 def __init__(self, layers_dims, activations_fnc, type_="classif"):
 self.type_ = type_
 self.W = [None] * (len(layers_dims)-1)
 self.b = [None] * (len(layers_dims)-1)
 self.out = [None] * (len(layers_dims)-1)
 self.layers_dims = layers_dims
 self.activations_fnc = activations_fnc
 for i in range(len(layers_dims)-1):
 self.b[i] = np.random.randn(layers_dims[i+1]).reshape(1, layers_dims[i+1])
 self.W[i] = np.random.randn(layers_dims[i], layers_dims[i+1])
 
 def sigmoid(self, x):
 return 1.0 / (1.0 + np.exp(-x))
 
 def tanh(self, x):
 return np.tanh(x)
 
 def relu(self, x):
 return np.maximum(x, 0)
 
 def identity(self, x):
 return x
 
 def softsign(self,x):
 return x / (1 + np.abs(x))
 
 def sin(self, x):
 return np.sin(x)
 
 def cos(self, x):
 return np.cos(x)
 
 def softmax(self, x):
 exp_scores = np.exp(x)
 out = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
 return out
 
 def activation(self, x, t):
 t = t.lower()
 if t == "sigmoid":
 return self.sigmoid(x)
 elif t == "tanh":
 return self.tanh(x)
 elif t == "relu":
 return self.relu(x)
 elif t == "identity":
 return self.identity(x)
 elif t == "softsign":
 return self.softsign(x)
 elif t == "sin":
 return self.sin(x)
 elif t == "cos":
 return self.cos(x)
 elif t == "softmax":
 return self.softmax(x)
 else:
 raise Exception("Non-supported activation function {}".format(t))
 
 def multiply(self, x, W):
 m = np.dot(x, W)
 return m
 
 def add(self, m, b):
 return m + b
 
 def forward(self, x):
 input_ = x
 for i, activation in enumerate(self.activations_fnc) :
 weighted_sum = self.multiply(x=input_, W=self.W[i])
 weighted_sum = self.add(m=weighted_sum, b=self.b[i])
 input_ = self.out[i] = self.activation(x=weighted_sum, t=activation)
使用神經網路生成抽象隨機藝術

注意,權重初始化是用np.random.randn()函式和定義類方法forward()完成的計算從上一層到下一層的正向傳播。僅實現這些類方法就足夠了。這個類不包括反向傳播和訓練神經網路分類/迴歸模型所需的任何東西,因為這裡的目的在於理解藝術生成過程。

為隨機藝術生成輸入引數

由於使用者必須定義生成的影像的大小,因此image_height和image_width是必需的。

此外,必須定義使用哪種顏色模式以及是否應該使用alpha通道。

對於輸出影像,將使用numpy.ndarray來表示影像。影像的形狀將是( img_height, img_width, 4 )。如果您熟悉影像處理或卷積神經網路,通常會看到形狀( img_height, img_width, 3 )。如果我們有一個灰度影像,影像實際上是一個2D-tensor。對於灰度影像,我們只需將初始灰度的每個值複製到RGB影像第三軸的每個維數中,即可得到RGB形狀,例如通過np.resize:

使用神經網路生成抽象隨機藝術

到目前為止還沒有問題。但是我們生成的影像在三維中有4個通道。這來自於alpha通道的建模。預設情況下,我們只建立這個定義的影像形狀,但如果我們不想包含影像中每個畫素的alpha-channel-random-modelling,輸出的alpha值將是1(意思是完全不透明度)。RGBA變成了RGB。

對於“CMYK”、“HSV”、“HSL”(輸出範圍均為[0,1])的顏色模式,這些值將轉換為RGB格式,因為對於影像顯示和儲存,我們假設是RGB影像,python實現如下。


def init_image(rows, cols):

 img = np.zeros(shape=(rows, cols, 4))
 return img
使用神經網路生成抽象隨機藝術


def hsv_to_rgb(h, s, v):

 ## hsw are between 0 and 1
 ## returns rgb between 0 and 1
 ## from: https://bgrins.github.io/TinyColor/docs/tinycolor.html
 h *= 6
 i = np.floor(h)
 f = h-i 
 p = v*(1-s)
 q = v*(1-f*s)
 t = v*(1-(1-f)*s)
 mod = int(i % 6)
 r = [v, q, p, p, t, v][mod]
 g = [t, v, v, q, p, p][mod]
 b = [p, p, t, v, v, q][mod]
 
 return r,g,b
def hsl_to_rgb(h, s, l):
 ## hsl are between 0 and 1
 ## returns rgb between 0 and 1
 
 def hue_to_rgb(p, q, t):
 if t < 0:
 t += 1
 if t > 1:
 t -= 1
 if t < 1/6:
 return p+(q-p)*6*t
 if t < 1/2:
 return q
 if t < 2/3:
 return p+(q-p)*(2/3-t)*6
 
 if s==0:
 r = g = b = l #achromatic
 else:
 if l < 0.5:
 q = l*(1+s)
 else:
 q = l+s-l*s
 p = 2*l-q
 r = hue_to_rgb(p, q, h + 1/3)
 g = hue_to_rgb(p, q, h)
 b = hue_to_rgb(p, q, h - 1/3)
 
 return r,g,b
使用神經網路生成抽象隨機藝術

神經網路架構

網路引數和輸入

對於神經網路,我們需要定義輸入維度、層數和啟用函式。

作為輸入,我們有一個5維向量。假設我們有一個大小為(256,256)的影像,把它看作一個二維矩陣。該演算法對每個畫素p_{i,j}計算r,g,b,a的值。

因此,在為生成的影像中的每個畫素生成r、g、b、a值作為輸入時(計算2-nested迴圈)我們取:

使用神經網路生成抽象隨機藝術

其中“factor=min(img height ,img width )”從而得到“input i,j =(x,y,r,z 1 ,z 2 )”。

將巢狀for迴圈中的x,y,r的值進行對映,從而建立結果影像中輸入資料的相互依賴和相關性:

使用神經網路生成抽象隨機藝術

注意,通過巢狀迴圈,x和y將是相互依賴的,如果生成的影像是正方形( img_height=img_width ),則輸入資料元件x和y始終小於1。

現在為生成藝術做layers和activation_fnc列表做準備工作。

注意,這也可以寫成硬編碼,而不是通過prep_nnet_arch函式定義:


def prep_nnet_arch(n_depth, n_size, activation, colormode, alpha):

 layers = [5] #x, y, r, z1, z2
 for i in range(n_depth):
 layers.append(n_size)
 
 colormode = colormode.lower()
 
 ### Output layer. Append number of output neurons depending on which colormode is selected
 if colormode in ["rgb", "hsv", "hsl"] : #RGB
 if not alpha:
 layers.append(3)
 else:
 layers.append(4)
 elif colormode == "cmyk":
 if not alpha:
 layers.append(4)
 else:
 layers.append(5) 
 elif colormode == "bw":
 if not alpha: 
 layers.append(1)
 else:
 layers.append(2)
 else:
 print("Inserted colormode '{}' is not part ob supported ones: [rgb, bw, cmyk, hsv, hsl]".format(colormode))
 raise Exception("Non-supported colormode {}".format(colormode))
 
 possibles = ["sigmoid", "tanh", "relu", "identity", "softsign", "sin", "cos", "softmax"]
 if not activation.lower() in possibles:
 print('defined activation {} not supported in {}'.format(activation, str(possibles)))
 return None
 
 activations_fnc = [activation] * (len(layers)-2)
 activations_fnc.append("sigmoid")
 
 return layers, activations_fnc
使用神經網路生成抽象隨機藝術

如果執行以下程式碼 :


n_depth = 4

n_size = 10
activation = "tanh"
colormode = "rgb"
alpha = True
layers, activations_fnc = prep_nnet_arch(n_depth, n_size, activation, colormode, alpha)
print("layers: {}".format(str(layers)))
print("activations: {}".format(str(activations_fnc)))
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

將生成以下神經網路:

使用神經網路生成抽象隨機藝術

用R,G,B,A的值填充影像


def get_color_at(nnet, x, y, r, z1, z2, colormode, alpha):

 input_ = np.array([x, y, r, z1, z2], dtype=np.float32).reshape(1, 5)
 nnet.forward(input_)
 
 colormode = colormode.lower()
 
 if colormode == "rgb": ## Output via sigmoid activation mapped into range [0,1]
 r = nnet.out[len(nnet.out)-1][0][0]
 g = nnet.out[len(nnet.out)-1][0][1]
 b = nnet.out[len(nnet.out)-1][0][2]
 a_index = 3
 elif colormode == "bw":
 r=g=b = nnet.out[len(nnet.out)-1][0][0]
 a_index = 1
 elif colormode == "cmyk":
 c = nnet.out[len(nnet.out)-1][0][0]
 m = nnet.out[len(nnet.out)-1][0][1]
 y = nnet.out[len(nnet.out)-1][0][2]
 k = nnet.out[len(nnet.out)-1][0][3]
 r = (1-c)*k
 g = (1-m)*k
 b = (1-y)*k
 a_index = 4
 elif colormode == "hsv":
 h = nnet.out[len(nnet.out)-1][0][0]
 s = nnet.out[len(nnet.out)-1][0][1]
 v = nnet.out[len(nnet.out)-1][0][2] 
 r, g, b = hsv_to_rgb(h, s, v)
 a_index = 3
 elif colormode == "hsl":
 h = nnet.out[len(nnet.out)-1][0][0]
 s = nnet.out[len(nnet.out)-1][0][1]
 l = nnet.out[len(nnet.out)-1][0][2] 
 r, g, b = hsl_to_rgb(h, s, l)
 a_index = 3
 else:
 print("Inserted colormode '{}' is not part ob supported ones: [rgb, bw, cmyk, hsv, hsl]".format(colormode))
 raise Exception("Non-supported colormode {}".format(colormode))
 if alpha: 
 # Since non blackmode [rgb, cmyk, hsv, hsl] values are mapped onto [0,1] the alpha channel is also between [0,1].
 #0=transparency, 1=opaque wrt. to overlaying
 a = 1-abs(2*nnet.out[len(nnet.out)-1][0][a_index]-1)
 a = 0.25 + 0.75*a
 else:
 a = 1.0
 
 return r, g, b, a
使用神經網路生成抽象隨機藝術

生成影像函式

函式的要點:

1、初始化輸出影像

2、初始化網路引數(層和啟用)

3、從影像中遍歷行和列以填充r、g、b、a的值


def generate_image(img_height, img_width, n_depth, n_size, activation, colormode, alpha, z1, z2,

 fname="netart.png", nnet_dict=None, save=False, show=False):
 factor = min(img_height, img_width)
 if nnet_dict is None:
 layers, activations_fnc = prep_nnet_arch(n_depth, n_size, activation, colormode, alpha)
 else:
 try:
 layers = nnet_dict["layers"]
 activations_fnc = nnet_dict["activations_fnc"]
 assert len(activations_fnc) == len(layers)-1
 assert layers[0] == 5
 assert activations_fnc[-1].lower() in ["sigmoid", "softmax"] 
 except Exception as e:
 print(e)
 
 nnet = NeuralNet(layers, activations_fnc) 
 img = init_image(img_height, img_width)
 for i in range(img_height):
 for j in range(img_width):
 x = i/factor - 0.5
 y = j/factor - 0.5
 r_ = np.sqrt(x*x + y*y)
 #Get RGBA values
 r, g, b, a = get_color_at(nnet, x=x, y=y, r=r_,
 z1=z1, z2=z2, colormode=colormode, alpha=alpha)
 #Populate the image
 img[i, j, 0] = r
 img[i, j, 1] = g
 img[i, j, 2] = b
 img[i, j, 3] = a
 
 if not show:
 matplotlib.use("Agg")
 
 plt.figure() 
 fig = plt.imshow(img, interpolation="bilinear", aspect="auto")
 plt.axis("off")
 fig.axes.get_xaxis().set_visible(False)
 fig.axes.get_yaxis().set_visible(False)
 
 if show:
 plt.show()
 
 if save:
 plt.imsave("{}".format(fname), img, format="png")
 
 return img
使用神經網路生成抽象隨機藝術

使用隨機設定執行演算法:


## This is fix

img_height = img_width = 256
activation = "tanh"
def randomize_configs():
 n_size = int(np.random.randint(low=12, high=20, size=1))
 n_depth = int(24 - n_size)
 colormode = np.random.choice(a=["rgb", "bw", "cmyk", "hsl", "hsv"], size=1)[0]
 alpha = bool(np.random.choice(a=[True, False], size=1))
 z1 = float(np.round(np.random.uniform(low=-1.0, high=1.0, size=1), decimals=5))
 z2 = float(np.round(np.random.uniform(low=-1.0, high=1.0, size=1), decimals=5))
 
 return n_size, n_depth, colormode, alpha, z1, z2
n_images = 10
if not os.path.exists("nb_output"):
 os.makedirs("nb_output")
使用神經網路生成抽象隨機藝術

使用隨機配置執行演算法10次,並將結果儲存在nb_output子目錄中:


for i in range(n_images):

 n_size, n_depth, colormode, alpha, z1, z2 = randomize_configs()
 print("Settings:")
 print("n_size: {}".format(n_size))
 print("n_depth: {}".format(n_depth))
 print("colormode: {}".format(colormode))
 print("alpha: {}".format(alpha))
 print("z1: {}".format(z1))
 print("z2: {}".format(z2))
 print("--------")
 fname = "nb_output/generated_{}_{}.png".format(colormode, i+1)
 print("Generated image: {}".format(i+1))
 start_time = time.time()
 generate_image(img_height, img_width, n_depth, n_size, activation, colormode, alpha, z1, z2,
 show=True, nnet_dict=None, fname=fname, save=True)
 delta = time.time() - start_time
 print("Generating image {} took {} seconds".format(i+1, delta))
使用神經網路生成抽象隨機藝術


設定:

n_size: 13
n_depth: 11
colormode: hsv
alpha: True
z1: -0.84136
z2: 0.97109
--------
生成影像: 1
使用神經網路生成抽象隨機藝術

生成影像1耗時4.51595664024353秒


設定:

n_size: 13
n_depth: 11
colormode: rgb
alpha: True
z1: -0.72624
z2: -0.19368
--------
生成影像: 2
使用神經網路生成抽象隨機藝術

生成影像2耗時4.032962322235107秒


設定:

n_size: 14
n_depth: 10
colormode: bw
alpha: False
z1: 0.14255
z2: -0.38622
--------
生成影像: 3
使用神經網路生成抽象隨機藝術

生成影像3耗時3.508967638015747秒


設定:

n_size: 12
n_depth: 12
colormode: rgb
alpha: True
z1: -0.39475
z2: -0.67197
--------
生成影像: 4
使用神經網路生成抽象隨機藝術

生成影像4耗時4.089961051940918秒


設定:

n_size: 12
n_depth: 12
colormode: hsl
alpha: True
z1: 0.95898
z2: -0.00935
--------
生成影像: 5
使用神經網路生成抽象隨機藝術

生成影像5耗時4.7219579219818115秒


設定:

n_size: 18
n_depth: 6
colormode: bw
alpha: False
z1: 0.30633
z2: 0.09442
--------
生成影像: 6
使用神經網路生成抽象隨機藝術

生成影像6耗時2.670975923538208秒


設定:

n_size: 19
n_depth: 5
colormode: bw
alpha: True
z1: 0.91837
z2: -0.34998
--------
生成影像: 7
使用神經網路生成抽象隨機藝術

生成影像7耗時2.580977201461792秒


設定:

n_size: 15
n_depth: 9
colormode: hsl
alpha: True
z1: -0.385
z2: 0.92596
--------
生成影像: 8
使用神經網路生成抽象隨機藝術

生成影像8耗時4.372961521148682秒


設定:

n_size: 18
n_depth: 6
colormode: bw
alpha: False
z1: 0.72774
z2: -0.89946
--------
生成影像: 9
使用神經網路生成抽象隨機藝術

生成影像9耗時2.6779751777648926秒


設定:

n_size: 15
n_depth: 9
colormode: cmyk
alpha: True
z1: 0.14114
z2: 0.49769
--------
生成影像: 10
使用神經網路生成抽象隨機藝術

生成影像10耗時3.770965099334717秒

生成具有定義的網路引數的影像

如上所述,可以通過層列表來定義隱藏層尺寸和隱藏層內的神經元。

確保層列表的第一個元素是5(因為有5個輸入值)。因此層[0]= 5。

在CMYK顏色模式的情況下,輸出神經元的大小應該是4。因此層[-1]= 4。

對於RGB、B&W、HSV、HSL等顏色模式,輸出神經元均為3。因此層[-1]= 3。

如果alpha通道也被使用,輸出層需要被1填充。因此層[-1] +=1。


img_height = img_width = 512

colormode = "cmyk"
alpha = False
activations_fnc = ["tanh", "softsign", "identity", "tanh", "sigmoid"]
layers = [5, 10, 8, 18, 13, 4]
if alpha:
 layers[-1] += 1
nnet_dict = {"layers":layers, "activations_fnc":activations_fnc}
z1 = -0.99
z2 = 0.85
start_time = time.time()
generate_image(img_height=img_height, img_width=img_width,
 n_depth=None, n_size=None, activation=None,
 colormode=colormode, alpha=alpha, z1=z1, z2=z2,
 nnet_dict=nnet_dict,
 show=True, save=False)
delta = time.time() - start_time
print("Generating image took {} seconds".format(delta))
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

生成影像耗時9.125910997390747秒


img_height = img_width = 512

colormode = "hsv"
alpha = True
activations_fnc = ["tanh", "softsign", "relu", "tanh", "sigmoid"]
layers = [5, 10, 20, 18, 25, 3]
if alpha:
 layers[-1] += 1
nnet_dict = {"layers":layers, "activations_fnc":activations_fnc}
z1 = -0.45
z2 = 0.66
start_time = time.time()
generate_image(img_height=img_height, img_width=img_width,
 n_depth=None, n_size=None, activation=None,
 colormode=colormode, alpha=alpha, z1=z1, z2=z2,
 nnet_dict=nnet_dict,
 show=True, save=False)
delta = time.time() - start_time
print("Generating image took {} seconds".format(delta))
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

生成影像耗時11.697889804840088秒


img_height = img_width = 512

colormode = "bw"
alpha = False
activations_fnc = ["tanh", "softsign", "softsign", "tanh", "sigmoid"]
layers = [5, 10, 30, 40, 50, 3]
if alpha:
 layers[-1] += 1
nnet_dict = {"layers":layers, "activations_fnc":activations_fnc}
z1 = 0.255
z2 = 0.900
start_time = time.time()
generate_image(img_height=img_height, img_width=img_width,
 n_depth=None, n_size=None, activation=None,
 colormode=colormode, alpha=alpha, z1=z1, z2=z2,
 nnet_dict=nnet_dict,
 show=True, save=False)
delta = time.time() - start_time
print("Generating image took {} seconds".format(delta))
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

生成影像耗時9.826904296875秒


img_height = img_width = 512

colormode = "hsl"
alpha = False
activations_fnc = ["tanh", "sin", "cos", "tanh", "sigmoid"]
layers = [5, 20, 10, 30, 40, 3]
if alpha:
 layers[-1] += 1
nnet_dict = {"layers":layers, "activations_fnc":activations_fnc}
z1 = 0.255
z2 = 0.900
start_time = time.time()
generate_image(img_height=img_height, img_width=img_width,
 n_depth=None, n_size=None, activation=None,
 colormode=colormode, alpha=alpha, z1=z1, z2=z2,
 nnet_dict=nnet_dict,
 show=True, save=False)
delta = time.time() - start_time
print("Generating image took {} seconds".format(delta))
使用神經網路生成抽象隨機藝術

使用神經網路生成抽象隨機藝術

生成影像耗時9.72290325164795秒


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29829936/viewspace-2636099/,如需轉載,請註明出處,否則將追究法律責任。

相關文章