理论上神经网络可以拟合任意的函数(应该用逼近更贴切一些)。
定义好神经网,送进去x和y,模型就学完了。但实际上,远没有这么简单。
不信,我们来看个简单例子。拟合一个y=cos(x),这样一个简单的函数。
这似乎是一个简单的不能再简单的问题。
下图这是循环1000次的效果,次数越多,逼近的越好。
代码如下:
import numpy as np
from keras import losses
from keras.layers import Input,Dense
from keras.models import Model
import matplotlib.pyplot as plt
np.random.seed(1024)
def get_data(startvalue_of_x,endValue_of_x,step_value,function):
dataX=np.arange(startvalue_of_x,endValue_of_x,step_value)
dataX=dataX.reshape(len(dataX),1)
dataY=function(dataX)
return dataX,dataY
def get_dense_model(unitsCount,activationFunction):
input=Input(shape=(1,),name="input")
dense=Dense(units=unitsCount,name="dense",activation=activationFunction)(input)
output=Dense(units=1,name="denseOutput")(dense)
model=Model(input,output)
return model
def run_train_and_test(startvalue_of_x,endValue_of_x,step_value,function,unitsCount,activationFunctionName):
X,Y=get_data(startvalue_of_x,endValue_of_x,step_value,function)
model=get_dense_model(unitsCount,activationFunctionName)
model.compile(optimizer="adam", loss=losses.mean_squared_error, metrics=[losses.mean_absolute_error])
model.fit(X,Y,batch_size=50,epochs=10000)
result=model.predict(X)
plt.plot(X,Y)
plt.plot(X,result,'--')
plt.show()
if __name__ == "__main__":
run_train_and_test(0,2*np.pi,0.01,np.cos,100,"relu")
一个周期没问题,接下来我们将最后一行代码,x的范围调整为[0,10*np.pi]之间,然后神奇的事情出现了。如下图所示,他只拟合了前面的一部分,why?循环1000次,难道不应该每个点都是一样的权重吗?为什么优先优化前面的点?这个问题至今没有想清楚。
循环10000次,是这样的效果。
更换激活函数,选择不一样的初始化,增加训练次数,增加层数,都能产生较为明显的变化。感兴趣可以参考一下代码,实现几个函数的模拟:
import numpy as np
from keras import losses
from keras.layers import Input,Dense
from keras.models import Model
import matplotlib.pyplot as plt
#x的取值范围
np.random.seed(np.power(2,10))
def get_data(startvalue_of_x,endValue_of_x,step_value,function):
dataX=np.arange(startvalue_of_x,endValue_of_x,step_value)
dataX=dataX.reshape(len(dataX),1)
dataY=function(dataX)
return dataX,dataY
def get_dense_model(unitsCount,activationFunction):
input=Input(shape=(1,),name="input")
dense=Dense(units=unitsCount,name="dense",activation=activationFunction)(input)
output=Dense(units=1,name="denseOutput")(dense)
model=Model(input,output)
return model
def get_dense_model_two_layer(unitsCount,activationFunction1,activationFunction2):
input=Input(shape=(1,),name="input")
dense1=Dense(units=unitsCount,name="dense1",activation=activationFunction1)(input)
dense2=Dense(units=unitsCount,name="dense2",activation=activationFunction2)(dense1)
output=Dense(units=1,name="denseOutput")(dense2)
model=Model(input,output)
return model
def run_train_and_test(startvalue_of_x,endValue_of_x,step_value,function,unitsCount,activationFunctionName):
X,Y=get_data(startvalue_of_x,endValue_of_x,step_value,function)
model=get_dense_model(unitsCount,activationFunctionName)
model.compile(optimizer="adam", loss=losses.mean_absolute_error, metrics=[losses.mean_absolute_error])
model.fit(X,Y,batch_size=50,epochs=1000)
result=model.predict(X)
plt.plot(X,Y)
plt.plot(X,result,'--')
plt.show()
def Pow_x_2(x):
return np.power(x,2)
def fun1(x):
return np.power(x,2)*np.cos(x)*np.sin(x)-20*x-300
def fun2(x):
return np.power(x,-1)
def run_train_and_test(startvalue_of_x,endValue_of_x,step_value,function,unitsCount,activationFunctionName1,activationFunctionName2):
X,Y=get_data(startvalue_of_x,endValue_of_x,step_value,function)
model=get_dense_model_two_layer(unitsCount,activationFunctionName1,activationFunctionName2)
model.compile(optimizer="adam", loss=losses.mean_squared_error, metrics=[losses.mean_absolute_error])
model.fit(X,Y,batch_size=50,epochs=200)
result=model.predict(X)
print(X)
print(Y)
plt.plot(X,Y)
plt.plot(X,result,'--')
plt.show()
if __name__ == "__main__":
run_train_and_test(0,10*np.pi,0.01,np.cos,100,"softplus","softplus")#cos
run_train_and_test(0,10*np.pi,0.01,Pow_x_2,100,"softplus","softplus")#x的平方
run_train_and_test(1,1000,1.0,fun2,100,"softplus","softplus")#1/x
run_train_and_test(0,10*np.pi,0.01,fun1,100,"softplus","softplus")#复杂一点的函数
如果要拟合更为复杂的函数,实践上应该将每个变量先经过一个神经网变换,再合并送入网络效果会更好一些。比如以下代码,将六个输入先经过神经网变换,然后合并成一个newinput,再作为输入去拟合,效果比较好。
def get_convert_x_model():
input=Input(shape=(1,))
dense11=Dense(activation='softplus',units=100)(input)
dense12=Dense(activation='softplus',units=100)(dense11)
newinput1=Dense(units=100)(dense12)
model=Model(input,newinput1)
return model
input1=Input(shape=(1,))
input2=Input(shape=(1,))
input3=Input(shape=(1,))
input4=Input(shape=(1,))
input5=Input(shape=(1,))
input6=Input(shape=(1,))
newinput1=get_convert_x_model()(input1)
newinput2=get_convert_x_model()(input2)
newinput3=get_convert_x_model()(input3)
newinput4=get_convert_x_model()(input4)
newinput5=get_convert_x_model()(input5)
newinput6=get_convert_x_model()(input6)
newinput=[newinput1,newinput2,newinput3,newinput4,newinput5,newinput6]
newinput=keras.layers.concatenate(newinput,axis=-1)