Closed eeeeeeeeeee555 closed 5 years ago
skorch expects datasets to yield (X, y)
pairs. In your example you are returning more than two values in __getitem__
, namely [self.y[idx], self.cont_X[idx], self.cat_X[idx]]
which then breaks the training loop since it tries to unpack three values (y, x, cat)
into (X, y)
.
It looks to me as though you want to have multiple features in X
and one target in y
, so you might want to do something like this in your dataset:
def __getitem__(self, idx):
return {'X': self.cont_X[idx], 'cat': self.cat_X[idx]}, self.y[idx]
The forward
method of your neural net module will then be called with X
and cat
parameters, e.g.:
class MyNetModule(nn.Module):
def forward(self, X, cat):
pass
Note that in your case it might not even be necessary to create your own dataset since skorch supports pandas DataFrame
s just fine. You might get away with just creating a dataframe with your relevant columns and passing it as X
. The behavior is the same as above but the parameter names to forward
will correspond to your dataframe's column names.
Thank you for detail answering ! :+1: I want to create my own dataset because I want to differentiate categorical columns and continuous columns. These categorical columns will be passed through the embedding layers in the model. Yes, I will try your way and tell how is it going on
def __getitem__(self, idx):
return {'X': self.cont_X[idx], 'cat': self.cat_X[idx]}, self.y[idx]
Hi, I changed in TabularDataset:
def __getitem__(self, idx):
# generates one sample of data
return [self.cont_X[idx], self.cat_X[idx]], self.y[idx]
and in model:
def forward(self, X):
cont_data = X[0]
cat_data = X[1]
It worked but I got nan value like this. Do you know what cause problem ?
epoch train_loss valid_loss dur
------- ------------ ------------ ------
1 nan nan 0.1560
2 nan nan 0.1670
3 nan nan 0.1610
4 nan nan 0.1590
5 nan nan 0.1490
.........
result = self.forward(*input, **kwargs)
TypeError: forward() got an unexpected keyword argument 'cat'
This is full code:
from skorch import NeuralNetRegressor
from skorch.dataset import Dataset
import torch
import torch.nn as nn
import torch.nn.functional as F
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder
class TabularDataset(Dataset):
def __init__(self, data, cat_cols=None, output_col=None):
self.n = data.shape[0]
if output_col:
self.y = data[output_col].astype(np.float32).values.reshape(-1, 1)
else:
self.y = np.zeros((self.n, 1))
self.cat_cols = cat_cols if cat_cols else []
self.cont_cols = [col for col in data.columns
if col not in self.cat_cols + [output_col]]
if self.cont_cols:
self.cont_X = data[self.cont_cols].astype(np.float32).values
else:
self.cont_X = np.zeros((self.n, 1))
if self.cat_cols:
self.cat_X = data[self.cat_cols].astype(np.int64).values
else:
self.cat_X = np.zeros((self.n, 1))
def __len__(self):
# Denotes the total number of sampoes
return self.n
def __getitem__(self, idx):
# generates one sample of data
return [self.cont_X[idx], self.cat_X[idx]], self.y[idx]
class FeedForwardNN(nn.Module):
def __init__(self, emb_dims, no_of_cont, lin_layer_sizes,
output_size, emb_dropout, lin_layer_dropouts):
"""
Parameters
----------
emb_dims: List of two element tuples
This list will contain a two element tuple for each
categorical feature. The first element of a tuple will
denote the number of unique values of the categorical
feature. The second element will denote the embedding
dimension to be used for that feature.
no_of_cont: Integer
The number of continuous features in the data.
lin_layer_sizes: List of integers.
The size of each linear layer. The length will be equal
to the total number
of linear layers in the network.
output_size: Integer
The size of the final output.
emb_dropout: Float
The dropout to be used after the embedding layers.
lin_layer_dropouts: List of floats
The dropouts to be used after each linear layer.
"""
super().__init__()
# Embedding layers
self.emb_layers = nn.ModuleList([nn.Embedding(x, y)
for x, y in emb_dims])
no_of_embs = sum([y for x, y in emb_dims])
self.no_of_embs = no_of_embs
self.no_of_cont = no_of_cont
# Linear Layers
first_lin_layer = nn.Linear(self.no_of_embs + self.no_of_cont,
lin_layer_sizes[0])
self.lin_layers = \
nn.ModuleList([first_lin_layer] + \
[nn.Linear(lin_layer_sizes[i], lin_layer_sizes[i + 1])
for i in range(len(lin_layer_sizes) - 1)])
for lin_layer in self.lin_layers:
nn.init.kaiming_normal_(lin_layer.weight.data)
# Output Layer
self.output_layer = nn.Linear(lin_layer_sizes[-1],
output_size)
nn.init.kaiming_normal_(self.output_layer.weight.data)
# Batch Norm Layers
self.first_bn_layer = nn.BatchNorm1d(self.no_of_cont)
self.bn_layers = nn.ModuleList([nn.BatchNorm1d(size)
for size in lin_layer_sizes])
# Dropout Layers
self.emb_dropout_layer = nn.Dropout(emb_dropout)
self.droput_layers = nn.ModuleList([nn.Dropout(size)
for size in lin_layer_dropouts])
def forward(self, X):
cont_data = X[0]
cat_data = X[1]
if self.no_of_embs != 0:
x = [emb_layer(cat_data[:, i])
for i, emb_layer in enumerate(self.emb_layers)]
x = torch.cat(x, 1)
x = self.emb_dropout_layer(x)
if self.no_of_cont != 0:
normalized_cont_data = self.first_bn_layer(cont_data)
if self.no_of_embs != 0:
x = torch.cat([x, normalized_cont_data], 1)
else:
x = normalized_cont_data
for lin_layer, dropout_layer, bn_layer in \
zip(self.lin_layers, self.droput_layers, self.bn_layers):
x = F.relu(lin_layer(x))
x = bn_layer(x)
x = dropout_layer(x)
x = self.output_layer(x)
return x
# Read data
data = pd.read_csv("data/train.csv", usecols=["SalePrice", "MSSubClass", "MSZoning", "LotFrontage", "LotArea",
"Street", "YearBuilt", "LotShape", "1stFlrSF", "2ndFlrSF"]).dropna()
categorical_features = ["MSSubClass", "MSZoning", "Street", "LotShape", "YearBuilt"]
output_feature = "SalePrice"
# Label Encode Categorial Features
label_encoders = {}
for cat_col in categorical_features:
label_encoders[cat_col] = LabelEncoder()
data[cat_col] = label_encoders[cat_col].fit_transform(data[cat_col])
# feed Forward NN
cat_dims = [int(data[col].nunique()) for col in categorical_features]
emb_dims = [(x, min(50, (x + 1) // 2)) for x in cat_dims]
net = FeedForwardNN(emb_dims, no_of_cont=4, lin_layer_sizes=[50, 100],
output_size=1, emb_dropout=0.04,
lin_layer_dropouts=[0.001, 0.01])
# Fit
ds = TabularDataset(data=data, cat_cols=categorical_features,
output_col=output_feature)
X = data.drop(['SalePrice'], axis=1)
y = data['SalePrice'].values.reshape(-1, 1)
net = NeuralNetRegressor(
net,
max_epochs=5,
lr=0.1,
dataset=ds
)
net.fit(X, y)
It is hard to debug why you are getting nans. Did you get good results with other, similar models? When using regression, it is often a good practice to scale the target data, depending on how it is distributed (e.g. log). Also, did you try a lower learning rate? If all that doesn't help, you should add a debugger to your forward method and see if any of the intermediate values look suspicious (very large or small).
TypeError: forward() got an unexpected keyword argument 'cat'
This happens when you pass in data as a dictionary or DataFrame
-- skorch will then treat the keys/columns as keywords. I don't see that in your code.
Thank you ! I will try your advice :)
@truongtamgio Was your issue solved?
Closing this since the solution can be found on stackoverflow: https://stackoverflow.com/questions/53386245/why-skorch-show-nan-in-the-every-epoch
def extract_video(param, root_dir, crops_dir):
#for key,value in enumerate(param):
#video, bboxes_path =key,value
#print(video,bboxes_path)
video, bboxes_path = map(int,param)
with open(bboxes_path,'r') as bbox_f:
bboxes_dict = json.load(bbox_f)
capture = cv2.VideoCapture(video)
frames_num = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
print(frames_num)
for i in range(frames_num):
capture.grab()
if i % 10 != 0:
continue
success, frame = capture.retrieve()
if not success or str(i) not in bboxes_dict:
continue
id = os.path.splitext(os.path.basename(video))[0]
crops = []
bboxes = bboxes_dict[str(i)]
if bboxes is None:
continue
for bbox in bboxes:
xmin, ymin, xmax, ymax = [int(b * 2) for b in bbox]
w = xmax - xmin
h = ymax - ymin
p_h = h // 3
p_w = w // 3
crop = frame[max(ymin - p_h, 0):ymax + p_h, max(xmin - p_w, 0):xmax + p_w]
h, w = crop.shape[:2]
crops.append(crop)
img_dir = os.path.join(root_dir, crops_dir, id)
os.makedirs(img_dir, exist_ok=True)
for j, crop in enumerate(crops):
cv2.imwrite(os.path.join(img_dir, "{}_{}.png".format(i, j)), crop)
def get_video_paths(root_dir): paths = [] for json_path in glob(root_dir):
dir = Path(json_path)
#print(dir)
with open(json_path,'r') as f:
metadata = json.load(f)
#print(metadata)
for k, v in metadata.items():
#print(k);
#print(v)
original = v.get("original", None)
if not original:
original = k
#print(original)
bboxes_path = os.path.join(root_dir, "boxes", original[:-4])
#print(bboxes_path)
#if not os.path.exists(bboxes_path):
#continue
#print(dir)
#print(k)
#print(bboxes_path)
paths.append((os.path.join(dir,k), bboxes_path))
#print(paths)
return paths
root_dir=data_folder crops_dir=root_save_image params = get_video_paths(root_dir_json) print(len(params)) params
if name == 'main': extract_video(params,root_dir,crops_dir)
@Qyum I suppose that you get the error message indicated at the top, which is why you posted your code. For us to be able to help you, could you please:
Thanks
Hi, I created TabularDataset base on Dataset class of Skorch then try to train but I got this error. Do you know how to fix it ? Thank you :)
And this is some part of my code. For full code, please access: Here I am using this data for training: house-prices-advanced-regression
.....