Комментарии 19
Действительно исследование и выглядит интересно, хорошо что математика осталась в pdf) У меня исследовательский вопрос: r7 и r19, если их сделать r1000 и r1 или рядом подряд, меняется время, точность? Второй вопрос, а что если похожее изображение добавить, но с инверсией например, или урезать/добавить в "яркости".
На иллюстрации r7 и r19 были выбраны просто для примера. В реальности они перемешиваются случайным образом но с одинаковым seed, чтобы была повторяемость и разные алгоритмы были в полностью одинаковых условиях. На счет изображения верно, - его можно поменять и результаты изменятся. Я пробовал с другими но похожими изображениями, результаты численно были другие, но общая картина была схожей. Но... поскольку в качестве изображения можно подать всё что угодно... не берусь судить, что может произойти в каждом конкретном случае)
Код открыт, про PCA сами ответили - честно. Но "сломал физику" звучит громко.
Ваш поворот обратимый, геометрию данных он не меняет. Деревья и бустинги мрут на нём потому, что умеют резать только по отдельным осям, их давняя слабость, не сенсация. А каскад ловит форму целиком, и ему всё равно, повёрнуты оси или нет: вы это сами показываете - RMSE с поворотом и без одинаковый. Получается, задача идеально легла под ваш метод, а не "порвала всех вообще".
Проверьте на смешивании посложнее (не ровный поворот, а перекос) и на рваной, негладкой функции - вот там и станет ясно, метод сильный или комната удачно выбрана.
Простите, если где-то написал неправильно, я только разбираюсь в этом) Написал ради того, чтобы выглядеть умным, мне статья очень понравилась и вечером буду перечитывать.
В этом тестировании он выступал в роли новичка‑аутсайдера. Но то, что он сделал с фаворитами, выглядит как читерство.
Есть ли независимые исследования метода? Все ссылки в конце даны на ваши собственные статьи, судя по всему. Честно было бы упомянуть это обстоятельство.
Это просто веселая статья, а не полноценное исследование. Но код открыт и всё, что в ней изложено, можно повторить. В одной из публикаций, на которую дана ссылка есть более полноценные исследования на известных бенчмарках. Код по ним тоже на github выложен. Математика подробно описана. Независимых исследований метода я пока не встречал (от публикаций на arXIv прошло лишь несколько месяцев) и это как раз то, что мне бы очень хотелось увидеть
Статья интересная, да и работа немаленькая
Однако, раз под капотом у полигармонического каскада работает расстояние (которое сохраняется при умножении на матрицу поворота), то было бы честно сделать другое преобразование с данными (например, аффинное сжатие/нелинейное преобразование). А то получается, что вы читерите: задача — рубить дерево, инструменты — чайник vs полка vs бензопила...
К слову, было забавно, что для svr взяли rbf ядро. Страшный вы человек, однако)))
В любом случае, спасибо за статью, было интересно
Улучшив "17_test_nnet3.py" думаю я разгромил PHC:
Device: cuda, Загрузка данных: rm=2000, rotate=True
Эпоха: 199 Ошибка обучения, RMSE: 0.0130, 0:00:02.410549 сек
Ошибка валидации, RMSE: 0.0143
0.0138, 0:10:01.892468, 0:00:03.905147
class Block(nn.Module):
def __init__(self, in_dim, expand, out_dim):
super().__init__()
self.i_l = nn.Linear(in_dim, in_dim * expand * 2)
self.o_l = nn.Linear(in_dim * expand, out_dim)
self.act = nn.Tanh()
def forward(self, x):
x, y = torch.chunk(self.i_l(x), 2, -1)
return self.o_l(self.act(x * y))
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(rm, 3)
self.fc2 = nn.Linear(3, 50)
self.fc3 = Block(50, 2, 50)
self.fc4 = Block(50, 2, 50)
self.fc5 = Block(50, 2, 50)
self.fc6 = Block(50, 2, 50)
self.fc10 = nn.Linear(50, 1)
def forward(self, x):
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
x = self.fc4(x)
x = self.fc5(x)
x = self.fc6(x)
x = self.fc10(x)
return x
scheduler = torch.optim.lr_scheduler.MultiplicativeLR(
optimizer, lambda epoch: 0.98
)
with torch.no_grad():
mean_loss /= batch_n
mean_loss = math.sqrt(mean_loss)
scheduler.step()Это все изменения.
Возник вопрос... но давай.. чтобы исключить любое непонимание или несоответствие... можно целиком весь 17_test_nnet3.py в новой версии? пожалуйста ;)
Скрытый текст
from torchvision import transforms
import datetime as dt
import torch
from torch.utils.data import Dataset, DataLoader
import torch.nn as nn
import math
import os
import im2data
fileIm = "1.png"
n = 240000
#rm = 2 # общее число признаков Раунд 1
#rm = 10 # общее число признаков Раунд 2,3
#rm = 500 # общее число признаков Раунд 4,5
#rm = 1000 # общее число признаков Раунд 6,7
rm = 2000 # общее число признаков Раунд 8,9
#rotate = False # поворот пространства признаков Раунд 1,2,4,6,8
rotate = True # поворот пространства признаков Раунд 3,5,7,9
EPOCHS = 200 # раунд 1,2,3
# EPOCHS = 400 # раунд 4,5
# EPOCHS = 1000 # раунд 6,7
# EPOCHS = 2000 # раунд 8,9
use_gpu = True # почему-то на моём железе, на cpu эта нейросеть работала быстрее
device = torch.device('cuda' if use_gpu and torch.cuda.is_available() else 'cpu')
print(f"Device: {device}, Загрузка данных: rm={rm}, rotate={rotate}")
im, X_train_full, X_test_new, X_train, y_train, X_test, y_test, rot_mat = \
im2data.obraz2d2(fileIm, n, rm, rotate=rotate)
class MyDataset(Dataset):
def __init__(self, X, y):
self.X = X
self.y = y
def __len__(self):
return self.X.shape[0]
def __getitem__(self, index):
return (self.X[index], self.y[index])
train = MyDataset(X_train,y_train)
test = MyDataset(X_test, y_test)
trainset = DataLoader(train, batch_size=500, shuffle=True)
testset = DataLoader(test, batch_size=30000, shuffle=False)
act = nn.Tanh()
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(rm, 3)
self.fc2 = nn.Linear(3, 30)
self.fc3 = nn.Linear(30, 30)
self.fc4 = nn.Linear(30, 30)
self.fc5 = nn.Linear(30, 30)
self.fc6 = nn.Linear(30, 30)
self.fc7 = nn.Linear(30, 30)
self.fc8 = nn.Linear(30, 30)
self.fc9 = nn.Linear(30, 30)
self.fc10 = nn.Linear(30, 30)
self.fc11 = nn.Linear(30, 1)
def forward(self, x):
x = self.fc1(x)
x = self.fc2(x)
x = act(x * self.fc3(x))
x = act(x + self.fc4(x))
x = act(x * self.fc5(x))
x = act(x + self.fc6(x))
x = act(x * self.fc7(x))
x = act(x + self.fc8(x))
x = act(x * self.fc9(x))
x = act(x + self.fc10(x))
return self.fc11(x)
# torch.manual_seed(0)
startL = dt.datetime.now()
model = Net().to(device)
torch.set_float32_matmul_precision('high')
model = torch.compile(model, mode="reduce-overhead")
# criterion = nn.CrossEntropyLoss()
criterion = nn.MSELoss()
# criterion = nn.NLLLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.003)
# optimizer = torch.optim.SGD(model.parameters(), lr=0.02, momentum=0.9)
scheduler = torch.optim.lr_scheduler.MultiplicativeLR(
optimizer, lambda epoch: 0.98
)
train_loss = []
val_loss = []
for epoch in range(EPOCHS):
start = dt.datetime.now()
print(f'Эпоха: {epoch}', end=' ')
mean_loss = 0
batch_n = 0
for X, y in trainset:
model.zero_grad()
output = model(X.to(device))
loss = criterion(output, y.to(device))
loss.backward()
optimizer.step()
mean_loss += loss
batch_n += 1
with torch.no_grad():
mean_loss /= batch_n
mean_loss = math.sqrt(mean_loss)
scheduler.step()
train_loss.append(mean_loss)
print(f'Ошибка обучения, RMSE: {mean_loss:.4f}, {dt.datetime.now() - start} сек')
mean_loss = 0
batch_n = 0
with torch.no_grad():
for X, y in testset:
output = model(X.to(device))
loss = criterion(output, y.to(device))
mean_loss += loss
batch_n += 1
mean_loss /= batch_n
mean_loss = math.sqrt(mean_loss)
val_loss.append(mean_loss)
print(f'Ошибка валидации, RMSE: {mean_loss:.4f}, lr: {optimizer.param_groups[0]["lr"]:.5f}')
train_time = dt.datetime.now() - startL
# Проверка результата
start = dt.datetime.now()
var = model(X_test_new.to(device))
pred_time = dt.datetime.now() - start
var[var<0] = 0
var[var>1] = 1
var = var.reshape(im.shape[0],im.shape[1])
var = var.to('cpu')
# Ошибка
rmse_full = torch.mean((var - im) ** 2) ** 0.5
rmse_full = rmse_full.item()
print(f'{rmse_full:.4f}, {train_time}, {pred_time}')Ещё улучшил. И сделал слои как в PHC.
Эпоха: 199 Ошибка обучения, RMSE: 0.0197, 0:00:02.415678 сек Ошибка валидации, RMSE: 0.0209, lr: 0.00005 0.0204, 0:09:54.106488, 0:00:03.664881
Попробовал задавать каскаду на валидацию более плоскую картинку, начал изолировать друг от друга объекты, потом заметил такую особенность - если для финального теста использовать перемешивание столбцов от обучения, то ошибка норм, а если другое, то не норм
П.С. потом понял, что модель и не должна в других признаках/столбцах искать ))
вот если использовать неизменный код (искаженная картинка сейчас одинакова с оригиналом)

если изменить код, добавить отдельное перемешивание

то вот так

Может у них одинаковый порядок должен быть? Но вроде как это отдельный тест и не должен быть связан с обучающим сетом
# Предсказания на полной тестовой выборке var = pc.FlammaPars(X_test_new, 50000)
возможно нужно не 10 эпох обучения на 10 признаках делать, попробую подольше
Так задача в том, чтобы аппроксимировать сложную функцию от двух признаков, двух столбцов в таблице (если пока не рассматриваем поворот). Но алгоритм не знает от каких. И в процессе обучения он должен их найти. А при тестировании нельзя в тестовом множестве эти два столбца трогать, переставлять с другими, иначе это как обман модели по отношению к тому чему её учили ).. Но вот другие признаки, шумовые, - их можно (и нужно) сгенерировать совсем по новому. Если правильно модель обучилась, то она должна их проигнорировать
Очень интересно. Но все, что взято из GitHub под Python 3.13.7 не работает
python 21_test_polyharm.py
Traceback (most recent call last):
File "f:\black-cat-main\21_test_polyharm.py", line 18, in <module>
fileIm = test_utils.get_image_path("1.png")
^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'test_utils' has no attribute 'get_image_path'
Вот итоговая валидация с повернутой картинкой

Странно почему при 0.259 так хорошо смотрится (а оказывается я не на том считал разницу, разница с оригиналом 0.038), хотя видны артефакты, а так выглядит очень похоже, получилось что-то вроде восстановления. Проверил не будет ли из белого шума создавать


Поиск черной кошки в 2000-мерной темной комнате. Турнир алгоритмов машинного обучения