import numpy as np, pdb import functools import torch import torch.nn as nn import torch.nn.functional as F from torch.utils.data import Dataset class SSLDataSet(Dataset): def __init__(self, data, clicks): super(SSLDataSet, self).__init__() self.data = data self.clicks = clicks def __getitem__(self, idx): return torch.tensor(self.data[idx], dtype=torch.float), torch.tensor(self.clicks[idx], dtype=torch.float) def __len__(self): return self.data.shape[0] class FTDataSet(Dataset): def __init__(self, data, label, clicks, actions=None, multi_label=False): super(FTDataSet, self).__init__() self.data = data self.label = label self.clicks = clicks self.multi_label = multi_label self.actions = actions def __getitem__(self, index): if self.multi_label: if self.actions is None: return (torch.tensor(self.data[index], dtype=torch.float), torch.tensor(self.label[index], dtype=torch.float), torch.tensor(self.clicks[index], dtype=torch.float)) else: return (torch.tensor(self.data[index], dtype=torch.float), torch.tensor(self.label[index], dtype=torch.float), torch.tensor(self.clicks[index], dtype=torch.float), torch.tensor(self.actions[index], dtype=torch.float)) else: return (torch.tensor(self.data[index], dtype=torch.float), torch.tensor(self.label[index], dtype=torch.long), torch.tensor(self.clicks[index], dtype=torch.float)) def __len__(self): return self.data.shape[0] # Resnet 1d def conv(in_planes, out_planes, stride=1, kernel_size=3): "convolution with padding 自动使用zeros进行padding" return nn.Conv1d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=(kernel_size - 1) // 2, bias=False) class ZeroPad1d(nn.Module): def __init__(self, pad_left, pad_right): super().__init__() self.pad_left = pad_left self.pad_right = pad_right def forward(self, x): return F.pad(x, (self.pad_left, self.pad_right)) class BasicBlock1d(nn.Module): expansion = 1 def __init__(self, inplanes, planes, stride=1, downsample=None): super().__init__() self.conv1 = conv(inplanes, planes, stride=stride, kernel_size=3) self.bn1 = nn.BatchNorm1d(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv(planes, planes, kernel_size=3) self.bn2 = nn.BatchNorm1d(planes) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class Bottleneck1d(nn.Module): """Bottleneck for ResNet52 ...""" expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super().__init__() kernel_size = 3 self.conv1 = nn.Conv1d(inplanes, planes, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm1d(planes) self.conv2 = nn.Conv1d(planes, planes, kernel_size=kernel_size, stride=stride, padding=(kernel_size - 1) // 2, bias=False) self.bn2 = nn.BatchNorm1d(planes) self.conv3 = nn.Conv1d(planes, planes * 4, kernel_size=1, bias=False) self.bn3 = nn.BatchNorm1d(planes * 4) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class ResNet1d(nn.Module): '''1d adaptation of the torchvision resnet''' def __init__(self, block, layers, kernel_size=3, input_channels=12, inplanes=64, fix_feature_dim=False, kernel_size_stem=None, stride_stem=2, pooling_stem=True, stride=2): super(ResNet1d, self).__init__() self.inplanes = inplanes layers_tmp = [] if kernel_size_stem is None: kernel_size_stem = kernel_size[0] if isinstance(kernel_size, list) else kernel_size # conv-bn-relu (basic feature extraction) layers_tmp.append(nn.Conv1d(input_channels, inplanes, kernel_size=kernel_size_stem, stride=stride_stem, padding=(kernel_size_stem - 1) // 2, bias=False)) layers_tmp.append(nn.BatchNorm1d(inplanes)) layers_tmp.append(nn.ReLU(inplace=True)) if pooling_stem is True: layers_tmp.append(nn.MaxPool1d(kernel_size=3, stride=2, padding=1)) for i, l in enumerate(layers): if i == 0: layers_tmp.append(self._make_block(block, inplanes, layers[0])) else: layers_tmp.append( self._make_block(block, inplanes if fix_feature_dim else (2 ** i) * inplanes, layers[i], stride=stride)) self.feature_extractor = nn.Sequential(*layers_tmp) def _make_block(self, block, planes, blocks, stride=1, kernel_size=3): down_sample = None # 注定会进行下采样 if stride != 1 or self.inplanes != planes * block.expansion: down_sample = nn.Sequential( nn.Conv1d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False), nn.BatchNorm1d(planes * block.expansion), ) layers = [] layers.append(block(self.inplanes, planes, stride, down_sample)) self.inplanes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def forward(self, x): return self.feature_extractor(x) def resnet1d14(inplanes, input_channels): return ResNet1d(BasicBlock1d, [2,2,2], inplanes=inplanes, input_channels=input_channels) def resnet1d18(**kwargs): return ResNet1d(BasicBlock1d, [2, 2, 2, 2], **kwargs) # MLP class MLP(nn.Module): def __init__(self, in_channels, hidden_channels, n_classes, bn = True): super(MLP, self).__init__() self.in_channels = in_channels self.n_classes = n_classes self.hidden_channels = hidden_channels self.fc1 = nn.Linear(self.in_channels, self.hidden_channels) self.fc2 = nn.Linear(self.hidden_channels, self.n_classes) self.ac = nn.ReLU() self.bn = nn.BatchNorm1d(hidden_channels) self.ln = nn.LayerNorm(hidden_channels) self.fc3 = nn.Linear(self.hidden_channels, self.hidden_channels) def forward(self, x): hidden = self.fc1(x) hidden = self.bn(hidden) hidden = self.ac(hidden) '''hidden = self.fc3(hidden) hidden = self.ln2(hidden) hidden = self.ac(hidden)''' out = self.fc2(hidden) return out # Time-steps features -> aggregated features class Flatten(nn.Module): def __init__(self): super(Flatten, self).__init__() def forward(self, tensor): b = tensor.size(0) return tensor.reshape(b, -1) class AdaptiveConcatPool1d(nn.Module): "Layer that concats `AdaptiveAvgPool1d` and `AdaptiveMaxPool1d`." def __init__(self, sz=None): "Output will be 2*sz or 2 if sz is None" super().__init__() sz = sz or 1 self.ap, self.mp = nn.AdaptiveAvgPool1d(sz), nn.AdaptiveMaxPool1d(sz) def forward(self, x): """x is shaped of B, C, T""" return torch.cat([self.mp(x), self.ap(x), x[..., -1:]], 1) def bn_drop_lin(n_in, n_out, bn, p, actn): "`n_in`->bn->dropout->linear(`n_in`,`n_out`)->`actn`" layers = list() if bn: layers.append(nn.BatchNorm1d(n_in)) if p > 0.: layers.append(nn.Dropout(p=p)) layers.append(nn.Linear(n_in, n_out)) if actn is not None: layers.append(actn) return layers def create_head1d(nf: int, nc: int, lin_ftrs=[512, ], dropout=0.5, bn: bool = True, act="relu"): lin_ftrs = [3 * nf] + lin_ftrs + [nc] activations = [nn.ReLU(inplace=True) if act == "relu" else nn.ELU(inplace=True)] * (len(lin_ftrs) - 2) + [None] layers = [AdaptiveConcatPool1d(), Flatten()] for ni, no, actn in zip(lin_ftrs[:-1], lin_ftrs[1:], activations): layers += bn_drop_lin(ni, no, bn, dropout, actn) layers += [nn.Sigmoid()] return nn.Sequential(*layers)