我试图对一些数据进行线性回归,并使用某些约束来获得特定的预测结果。我希望模型能够预测出前半部分的线性预测值,而后半部分的线性预测值在接近前半部分最后一个值时使用非常狭窄的范围(使用约束),类似于图中绿色线条的效果。
完整代码如下:
import numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom sklearn.linear_model import LinearRegressionfrom sklearn.model_selection import train_test_splitpd.options.mode.chained_assignment = None # default='warn'data = [5.269, 5.346, 5.375, 5.482, 5.519, 5.57, 5.593999999999999, 5.627000000000001, 5.724, 5.818, 5.792999999999999, 5.817, 5.8389999999999995, 5.882000000000001, 5.92, 6.025, 6.064, 6.111000000000001, 6.1160000000000005, 6.138, 6.247000000000001, 6.279, 6.332000000000001, 6.3389999999999995, 6.3420000000000005, 6.412999999999999, 6.442, 6.519, 6.596, 6.603, 6.627999999999999, 6.76, 6.837000000000001, 6.781000000000001, 6.8260000000000005, 6.849, 6.875, 6.982, 7.018, 7.042000000000001, 7.068, 7.091, 7.204, 7.228, 7.261, 7.3420000000000005, 7.414, 7.44, 7.516, 7.542000000000001, 7.627000000000001, 7.667000000000001, 7.821000000000001, 7.792999999999999, 7.756, 7.871, 8.006, 8.078, 7.916, 7.974, 8.074, 8.119, 8.228, 7.976, 8.045, 8.312999999999999, 8.335, 8.388, 8.437999999999999, 8.456, 8.227, 8.266, 8.277999999999999, 8.289, 8.299, 8.318, 8.332, 8.34, 8.349, 8.36, 8.363999999999999, 8.368, 8.282, 8.283999999999999]time = range(1,85,1) x=int(0.7*len(data))df = pd.DataFrame(list(zip(*[time, data])))df.columns = ['time', 'data']# print dfx=int(0.7*len(df))train = df[:x]valid = df[x:]models = []names = []tr_x_ax = []va_x_ax = []pr_x_ax = []tr_y_ax = []va_y_ax = []pr_y_ax = []time_model = []models.append(('LR', LinearRegression()))for name, model in models: x_train=df.iloc[:, 0][:x].values y_train=df.iloc[:, 1][:x].values x_valid=df.iloc[:, 0][x:].values y_valid=df.iloc[:, 1][x:].values model = LinearRegression() # poly = PolynomialFeatures(5) x_train= x_train.reshape(-1, 1) y_train= y_train.reshape(-1, 1) x_valid = x_valid.reshape(-1, 1) y_valid = y_valid.reshape(-1, 1) # model.fit(x_train,y_train) model.fit(x_train,y_train.ravel()) # score = model.score(x_train,y_train.ravel()) # print 'score', score preds = model.predict(x_valid) tr_x_ax.extend(train['data']) va_x_ax.extend(valid['data']) pr_x_ax.extend(preds) valid['Predictions'] = preds valid.index = df[x:].index train.index = df[:x].index plt.figure(figsize=(5,5)) # plt.plot(train['data'],label='data') # plt.plot(valid[['Close', 'Predictions']]) x = valid['data'] # print x # plt.plot(valid['data'],label='validation') plt.plot(valid['Predictions'],label='Predictions before',color='orange')y =range(0,58)y1 =range(58,84)for index, item in enumerate(pr_x_ax): if index >13: pr_x_ax[index] = pr_x_ax[13]pr_x_ax = list([float(i) for i in pr_x_ax])va_x_ax = list([float(i) for i in va_x_ax])tr_x_ax = list([float(i) for i in tr_x_ax])plt.plot(y,tr_x_ax, label='train' , color='red', linewidth=2)plt.plot(y1,va_x_ax, label='validation1' , color='blue', linewidth=2)plt.plot(y1,pr_x_ax, label='Predictions after' , color='green', linewidth=2)plt.xlabel("time")plt.ylabel("data")plt.xticks(rotation=45)plt.legend()plt.show()
如果你看到这张图:
标签:Predictions before
,模型在没有任何约束的情况下进行了预测(我不需要这种结果)。
标签:Predictions after
,模型在约束条件下进行了预测,但这是模型预测之后的结果,且所有值都等于最后一个值,索引为index = 71
,数值为item 8.56
。
我在第64行使用了for循环for index, item in enumerate(pr_x_ax):
,曲线从71秒到85秒是直线,如你所见,这是为了展示我希望模型如何工作。
我能否构建一个模型来替代for循环,得到相同的结果?
请提供您的建议
回答:
我认为你在问题中通过绘制绿色线条,实际上是希望训练模型能够预测出向右的线性水平转折。但当前训练的模型只是绘制了一条直的橙色线。
对于任何训练模型,无论算法和类型如何,为了学习某种异常行为的变化,模型至少需要一些这种异常变化的样本。或者至少在观察到的数据中应该有一些隐藏的含义指向这种异常变化的存在。
换句话说,为了让你的模型学习到绿色线条上的右转,模型的训练数据集中应该包含有这种右转的点。但你只取了数据的前70%(最左边的部分)作为训练数据,train = df[:int(0.7 * len(df))]
,而这些训练数据中没有这样的右转,这些训练数据看起来接近一条直线。
因此,你需要以不同的方式重新对数据进行抽样,将训练和验证数据分开——从整个X
范围中随机抽取70%的样本作为训练数据,其余的作为验证数据。这样,你的训练数据中也包括了那些右转的样本。
第二点是,LinearRegression
模型总是用一条单一的直线来建模预测,这种直线无法有右转。为了实现右转,你需要使用更复杂的模型。
模型实现右转的一种方法是使用分段线性模型,即由几条连接的直线组成。我在sklearn
中没有找到现成的分段线性模型,只能使用其他pip
模型。所以我决定实现一个简单的类PieceWiseLinearRegression
,它使用np.piecewise()
和scipy.optimize.curve_fit()
来建模分段线性函数。
下一张图展示了应用上述两个建议的结果,代码随后给出,即以不同的方式重新抽样数据集和建模分段线性函数。你当前的线性模型LR
仍然使用一条直的蓝色线进行预测,而我的分段线性模型PWLR2
,橙色线,由两段组成,并正确预测了右转:
为了清楚地看到一个PWLR2
图,我还做了下一张图:
我的类PieceWiseLinearRegression
在创建对象时只接受一个参数n
——用于预测的线性段数。上述图中使用了n = 2
。