我需要在Python中创建一个线性回归模型,但不使用scikit。你可以忽略输入部分,因为那部分是根据我提供的文件来的。我已经添加了我的完整代码,以防我犯了什么错误。
import pandas as pdimport numpy as npimport matplotlib.pyplot as mltfrom sklearn.cross_validation import train_test_split data = pd.read_csv("housing.csv", delimiter = ' ', skipinitialspace = True, names = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV'])df_x = data.drop('MEDV', axis = 1)df_y = data['MEDV']x_train, x_test, y_train, y_test = train_test_split(df_x.values, df_y.values, test_size = 0.2, random_state = 4)theta = np.zeros((1, 13))
在上面的代码中,我只是获取了输入并创建了一个名为theta的参数数组。
def costfn(x, y, theta): j = np.sum(x.dot(theta.T) - y) ** 2 / (2 * len(y)) return jdef gradient(x, y, theta, alpha, iterations): cost_history = [0] * iterations for i in range(iterations): h = theta.dot(x.T) #hypothesis loss = h - y #print(loss) g = loss.dot(x) / len(y) #print(g) theta = theta - alpha * g cost_history[i] = costfn(x, y, theta) #print(theta) return theta, cost_historytheta, cost_history = gradient(x_train, y_train, theta, 0.001, 1000)#print(theta)
我注释掉的所有行输出都是适当大小的nan值。
我使用了类似于这个博客上的逻辑。如果我错了,请告诉我。
回答:
总的来说,我认为你的代码是工作的。你观察到的情况很可能是由于alpha的设置过高,导致theta发散。在某个点上它会变成inf
或-inf
,之后在下一次迭代中你会得到NaN
值。我也遇到了同样的问题。
你可以通过一个简单的设置来验证这一点:
# 在你的函数中输出theta def gradient(x, y, theta, alpha, iterations): cost_history = [0] * iterations for i in range(iterations): h = theta.dot(x.T) #hypothesis #print('h:', h) loss = h - y #print('loss:', loss) g = loss.dot(x) / len(y) #print('g:', g) theta = theta - alpha * g print('theta:', theta) cost_history[i] = costfn(x, y, theta) #print(theta) return theta, cost_history# 使用一个简单的线性关系设置示例数据# 我们可以用不同的参数数量进行试验# 方便起见,加入一些噪声num_params= 2 # 你想要估计的参数数量(最多5个)# 取一些固定的参数(我们只取num_params个)real_params= [2.3, -0.1, 8.5, -1.8, 3.2]# 现在为选择的参数数量生成数据x_train= np.random.randint(-100, 100, size=(80, num_params))x_noise= np.random.randint(-100, 100, size=(80, num_params)) * 0.001y_train= (x_train + x_noise).dot(np.array(real_params[:num_params]))theta= np.zeros(num_params)
现在尝试使用高学习率
theta, cost_history = gradient(x_train, y_train, theta, 0.1, 1000)
你很可能会观察到,你的theta值的指数越来越大,直到最终达到inf
或-inf
。之后你会得到NaN
值。
但是,如果你将其设置为一个低值,如0.00001,你会看到它收敛:
theta: [ 0.07734451 -0.00357339]theta: [ 0.15208803 -0.007018 ]theta: [ 0.22431803 -0.01033852]theta: [ 0.29411905 -0.01353942]theta: [ 0.36157275 -0.01662507]theta: [ 0.42675808 -0.01959962]theta: [ 0.48975132 -0.02246712]theta: [ 0.55062617 -0.02523144]...theta: [ 2.29993382 -0.09981407]theta: [ 2.29993382 -0.09981407]theta: [ 2.29993382 -0.09981407]theta: [ 2.29993382 -0.09981407]
这与真实参数2.3
和-0.1
非常接近。
所以你可以尝试编写代码来调整学习率,使值更快收敛,并降低发散的风险。你还可以实现类似于早期停止的功能,如果误差不变或变化低于某个阈值,则停止对样本的迭代。
例如,你可以对你的函数进行以下修改:
def gradient( x, y, theta=None, alpha=0.1, alpha_factor=0.1 ** (1/5), change_threshold=1e-10, max_iterations=500, verbose=False): cost_history = list() if theta is None: # theta没有明确传递 # 所以初始化它 theta= np.zeros(x.shape[1]) last_loss_sum= float('inf') len_y= len(y) for i in range(1, max_iterations+1): h = theta.dot(x.T) #hypothesis loss = h - y loss_sum= np.sum(np.abs(loss)) if last_loss_sum <= loss_sum: # 误差没有减少 # 所以减小alpha alpha= alpha * alpha_factor if verbose: print(f'pass: {i:4d} loss: {loss_sum:.8f} / alpha: {alpha}') theta_old= theta g= loss.dot(x) / len_y if loss_sum <= last_loss_sum and last_loss_sum < float('inf'): # 只有当误差是有限的才应用变化 # 避免theta中出现无限值 theta = theta - alpha * g theta_change= np.sum(np.abs(theta_old - theta)) if theta_change < change_threshold: # 这可能看起来有点奇怪,但 # change_threshold的比较 # 考虑了theta和g之间的关系。请注意,如果theta比g大几个数量级, # 即使g本身很大,g也不会有影响。 # (我是指逐元素考虑g和theta) cost_history.append(costfn(x, y, theta)) break cost_history.append(costfn(x, y, theta)) last_loss_sum= loss_sum return theta, cost_history
这些更改解决了早期停止、自动调整alpha
和避免theta
变成无限值的问题。在最简单的情况下,你只需要传递X
和y
,所有其他参数都有默认值。如果你想看到每次迭代中的误差减少情况,请设置verbose=True
。