Как сделать ограниченную линейную регрессию — scikit learn?

#python #machine-learning #scikit-learn #linear-regression

#python #машинное обучение #scikit-learn #линейная регрессия

Вопрос:

Я пытаюсь выполнить линейную регрессию, используя некоторые ограничения, чтобы получить определенное предсказание. Я хочу, чтобы модель предсказывала половину линейного прогноза, а последняя половина линейного прогноза была близка к последнему значению в первой половине, используя очень узкий диапазон (с использованием ограничений), аналогичный зеленой линии на рисунке.

Полный код:

 import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
pd.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 df
x=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()
  

Если вы видите этот рисунок:

label: Predictions before , модель предсказала это без каких-либо ограничений (мне не нужен этот результат).

label: Predictions after , модель предсказала это в пределах ограничения, но это после предсказания модели, И все значения равны последнему значению at index = 71 , item 8.56 .

Я использовал цикл for for index, item in enumerate(pr_x_ax): в строке: 64, а кривая прямая со времени 71 до 85 секунд, как вы видите, чтобы показать вам, как мне нужна работа модели.

Могу ли я построить модель, дающую тот же результат вместо цикла for???

рисунок

Пожалуйста, ваши предложения

Комментарии:

1. Не могли бы вы сделать свой вопрос короче и более по существу? Я прочитал дважды, и я все еще не понимаю вопроса.

2. Я задал короткий вопрос.

3. @Algabri Похоже, что поворот вправо, который вы нарисовали на зеленой линии, вообще нельзя предсказать, учитывая только тренировочные точки красной линии. Чтобы такие повороты были предсказуемыми, вам нужно несколько тренировочных точек вокруг этого местоположения поворота. В противном случае прямо сейчас, обучая точки, данная модель просто считает, что все прогнозы должны быть аппроксимированы только прямой линией без поворотов.

4. @Algabri Чтобы получить хорошее решение, вам необходимо случайным образом отбирать данные для обучения и проверки. Вместо того, чтобы делать df[:x] , и df[x:] вам нужно поместить случайные (x, y) точки в обучающий набор и случайные для проверки, установленные в необходимых пропорциях, например, 70% для обучения и 30% для проверки. Тогда ваше обучение должно привести к правильным зеленым результатам.

5. @Algabri Также LinearRegression предсказывает данные только с помощью одной прямой линии, у нее не может быть поворотов, для кусочно-линейной аппроксимации, как вы хотите на зеленой линии, следует использовать более сложную модель, я постараюсь прочитать документы, чтобы найти такую модель.

Ответ №1:

Я ожидаю, что в вашем вопросе, рисуя зеленую линию, вы действительно ожидаете, что обученная модель будет предсказывать линейный горизонтальный поворот вправо. Но текущая обученная модель рисует только прямую оранжевую линию.

Для любой обученной модели любого алгоритма и типа верно, что для изучения некоторых необычных изменений в модели поведения необходимо иметь хотя бы несколько примеров этого необычного изменения. Или, по крайней мере, какой-то скрытый смысл в наблюдаемых данных должен указывать на наличие такого необычного изменения.

Другими словами, чтобы ваша модель узнала, что правый поворот на зеленой линии, модель должна иметь точки с этим правым поворотом в наборе обучающих данных. Но вы берете для обучающих данных только первые (крайние слева) 70% данных, train = df[:int(0.7 * len(df))] и эти обучающие данные не имеют таких поворотов вправо, и эти обучающие данные просто выглядят близко к одной прямой.

Итак, вам нужно повторно использовать ваши данные для обучения и проверки по-другому — случайным образом отбирать 70% выборок из всего диапазона X , а остальное идет на проверку. Так что в ваши обучающие образцы данных, которые делают правый поворот, также включены.

Во-вторых, LinearRegression модель всегда моделирует прогнозы только с одной прямой линией, и эта линия не может иметь прямых поворотов. Для того, чтобы иметь правильные повороты, вам нужна более сложная модель.

Один из способов, которым модель поворачивается вправо, — это быть кусочно-линейной, то есть иметь несколько соединенных прямых линий. Я не нашел готовых кусочно-линейных моделей внутри sklearn , только используя другие pip модели. Поэтому я решил реализовать свой собственный простой класс PieceWiseLinearRegression , который использует np.piecewise() и scipy.optimize.curve_fit() для моделирования кусочно-линейной функции.

На следующем рисунке показаны результаты применения двух упомянутых выше действий, после чего выполняется код, повторная выборка набора данных другим способом и моделирование кусочно-линейной функции. Ваша текущая линейная модель LR по-прежнему делает прогноз, используя только одну прямую синюю линию, в то время как моя кусочно-линейная PWLR2 оранжевая линия состоит из двух сегментов и правильно предсказывает правый поворот:

img

Чтобы четко видеть только один PWLR2 график, я тоже сделал следующее изображение:

введите описание изображения здесь

Мой класс PieceWiseLinearRegression по созданию объекта принимает только один аргумент n — количество линейных сегментов, которые будут использоваться для прогнозирования. Для изображения выше n = 2 было использовано.

 import sys, numpy as np, pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
np.random.seed(0)

class PieceWiseLinearRegression:
    @classmethod
    def nargs_func(cls, f, n):
        return eval('lambda '   ', '.join([f'a{i}'for i in range(n)])   ': f('   ', '.join([f'a{i}'for i in range(n)])   ')', locals())
        
    @classmethod
    def piecewise_linear(cls, n):
        condlist = lambda xs, xa: [(lambda x: (
            (xs[i] <= x if i > 0 else np.full_like(x, True, dtype = np.bool_)) amp;
            (x < xs[i   1] if i < n - 1 else np.full_like(x, True, dtype = np.bool_))
        ))(xa) for i in range(n)]
        funclist = lambda xs, ys: [(lambda i: (
            lambda x: (
                (x - xs[i]) * (ys[i   1] - ys[i]) / (
                    (xs[i   1] - xs[i]) if abs(xs[i   1] - xs[i]) > 10 ** -7 else 10 ** -7 * (-1, 1)[xs[i   1] - xs[i] >= 0]
                )   ys[i]
            )
        ))(j) for j in range(n)]
        def f(x, *pargs):
            assert len(pargs) == (n   1) * 2, (n, pargs)
            xs, ys = pargs[0::2], pargs[1::2]
            xa = x.ravel().astype(np.float64)
            ya = np.piecewise(x = xa, condlist = condlist(xs, xa), funclist = funclist(xs, ys)).ravel()
            #print('xs', xs, 'ys', ys, 'xa', xa, 'ya', ya)
            return ya
        return cls.nargs_func(f, 1   (n   1) * 2)
        
    def __init__(self, n):
        self.n = n
        self.f = self.piecewise_linear(self.n)

    def fit(self, x, y):
        from scipy import optimize
        self.p, self.e = optimize.curve_fit(self.f, x, y, p0 = [j for i in range(self.n   1) for j in (np.amin(x)   i * (np.amax(x) - np.amin(x)) / self.n, 1)])
        #print('p', self.p)
        
    def predict(self, x):
        return self.f(x, *self.p)

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 = list(range(1, 85))
df = pd.DataFrame(list(zip(time, data)), columns = ['time', 'data'])

choose_train = np.random.uniform(size = (len(df),)) < 0.8
choose_valid = ~choose_train

x_all = df.iloc[:, 0].values
y_all = df.iloc[:, 1].values
x_train = df.iloc[:, 0][choose_train].values
y_train = df.iloc[:, 1][choose_train].values
x_valid = df.iloc[:, 0][choose_valid].values
y_valid = df.iloc[:, 1][choose_valid].values
x_all_lin = np.linspace(np.amin(x_all), np.amax(x_all), 500)

models = []
models.append(('LR', LinearRegression()))
models.append(('PWLR2', PieceWiseLinearRegression(2)))
        
for imodel, (name, model) in enumerate(models):
    model.fit(x_train[:, None], y_train)
    x_all_lin_pred = model.predict(x_all_lin[:, None])
    plt.plot(x_all_lin, x_all_lin_pred, label = f'pred {name}')

plt.plot(x_train, y_train, label='train')
plt.plot(x_valid, y_valid, label='valid')
plt.xlabel('time')
plt.ylabel('data')
plt.legend()
plt.show()
  

Комментарии:

1. Большое вам спасибо за вашу помощь. Кажется, это решит мою проблему. Я пытаюсь запустить ваш код на python 2, срок действия которого истек в первом в этом году, но я все еще использую его, потому что этого требует физическая система роботов (для этого необходимо обновить всю систему). Я получил эту ошибку return eval(‘lambda ‘ ‘, ‘.join([‘a{i}’ для i в диапазоне (n)]) ‘: (‘ ‘, ‘. join([‘a{i}’для i в диапазоне (n)]) ‘)’, locals()) Файл «<string>», строка 1 лямбда a{i}, a{i}, a{i}, a{i}, a{i}, a{i}, a{i}, a{i}, a{i}: (a{i}, a{i}, a{i}, a{i}, a{i}, a{i}, a {i}) ^ Ошибка синтаксиса: недопустимый синтаксис

2. @Algabri вместо 'a{i}' того, чтобы в обоих местах использовать 'a%s' % i

3. Большое вам спасибо за вашу помощь от всего сердца. Вы действительно сэкономили мое время.