-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #126 from HazyResearch/learning_cleanup
Add sparse LR support and reorg disc model dir
- Loading branch information
Showing
12 changed files
with
258 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,14 @@ | ||
from fonduer.learning.disc_models.logistic_regression import LogisticRegression | ||
from fonduer.learning.disc_models.rnn.lstm import LSTM | ||
from fonduer.learning.disc_models.lstm import LSTM | ||
from fonduer.learning.disc_models.sparse_logistic_regression import ( | ||
SparseLogisticRegression | ||
) | ||
from fonduer.learning.gen_learning import GenerativeModel, GenerativeModelAnalyzer | ||
|
||
__all__ = ["GenerativeModel", "GenerativeModelAnalyzer", "LogisticRegression", "LSTM"] | ||
__all__ = [ | ||
"GenerativeModel", | ||
"GenerativeModelAnalyzer", | ||
"LogisticRegression", | ||
"LSTM", | ||
"SparseLogisticRegression", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
""" | ||
A sparse linear module. | ||
""" | ||
|
||
import math | ||
|
||
import torch | ||
import torch.nn as nn | ||
|
||
|
||
class SparseLinear(nn.Module): | ||
def __init__(self, num_features, num_classes, bias=False, padding_idx=0): | ||
|
||
super(SparseLinear, self).__init__() | ||
|
||
self.num_features = num_features | ||
self.num_classes = num_classes | ||
self.padding_idx = padding_idx | ||
|
||
self.weight = nn.Embedding( | ||
self.num_features, self.num_classes, padding_idx=self.padding_idx | ||
) | ||
if bias: | ||
self.bias = nn.Parameter(torch.Tensor(self.num_classes)) | ||
else: | ||
self.bias = None | ||
|
||
self.reset_parameters() | ||
|
||
def reset_parameters(self): | ||
stdv = 1. / math.sqrt(self.num_features) | ||
self.weight.weight.data.uniform_(-stdv, stdv) | ||
if self.bias is not None: | ||
self.bias.data.uniform_(-stdv, stdv) | ||
if self.padding_idx is not None: | ||
self.weight.weight.data[self.padding_idx].fill_(0) | ||
|
||
def forward(self, x, w): | ||
""" | ||
x : batch_size * length, the feature indices | ||
w : batch_size * length, the weight for each feature | ||
""" | ||
if self.bias is None: | ||
return (w.unsqueeze(2) * self.weight(x)).sum(dim=1) | ||
else: | ||
return (w.unsqueeze(2) * self.weight(x)).sum(dim=1) + self.bias |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file was deleted.
Oops, something went wrong.
152 changes: 152 additions & 0 deletions
152
src/fonduer/learning/disc_models/sparse_logistic_regression.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import numpy as np | ||
import torch | ||
|
||
from fonduer.learning.disc_learning import NoiseAwareModel | ||
from fonduer.learning.disc_models.layers.sparse_linear import SparseLinear | ||
from fonduer.learning.disc_models.utils import pad_batch | ||
|
||
|
||
class SparseLogisticRegression(NoiseAwareModel): | ||
def forward(self, x, w): | ||
""" | ||
Run forward pass. | ||
:param x: The input (batch) of the model | ||
""" | ||
return self.sparse_linear(x, w) | ||
|
||
def _check_input(self, X): | ||
""" | ||
Check input format. | ||
:param X: The input data of the model | ||
""" | ||
return isinstance(X, tuple) | ||
|
||
def _preprocess_data(self, X, Y=None, idxs=None, train=False): | ||
""" | ||
Preprocess the data: | ||
1. Convert sparse matrix to dense matrix. | ||
2. Update the order of candidates based on feature index. | ||
3. Select subset of the input if idxs exists. | ||
:param X: The input data of the model | ||
:param X: The labels of input data | ||
""" | ||
C, F = X | ||
print(F.shape) | ||
print(max(F.indices)) | ||
id2id = dict() | ||
for i in range(F.shape[0]): | ||
id2id[F.row_index[i]] = i | ||
|
||
C_ = [None] * len(C) | ||
for c in C: | ||
C_[id2id[c.id]] = c | ||
|
||
if idxs is None: | ||
if Y is not None: | ||
return ( | ||
[ | ||
( | ||
C_[i], | ||
F.indices[F.indptr[i] : F.indptr[i + 1]], | ||
F.data[F.indptr[i] : F.indptr[i + 1]], | ||
) | ||
for i in range(len(C_)) | ||
], | ||
Y, | ||
) | ||
else: | ||
return [ | ||
( | ||
C_[i], | ||
F.indices[F.indptr[i] : F.indptr[i + 1]], | ||
F.data[F.indptr[i] : F.indptr[i + 1]], | ||
) | ||
for i in range(len(C_)) | ||
] | ||
if Y is not None: | ||
return ( | ||
[ | ||
( | ||
C_[i], | ||
F.indices[F.indptr[i] : F.indptr[i + 1]], | ||
F.data[F.indptr[i] : F.indptr[i + 1]], | ||
) | ||
for i in idxs | ||
], | ||
Y[idxs], | ||
) | ||
else: | ||
return [ | ||
( | ||
C_[i], | ||
F.indices[F.indptr[i] : F.indptr[i + 1]], | ||
F.data[F.indptr[i] : F.indptr[i + 1]], | ||
) | ||
for i in idxs | ||
] | ||
|
||
def _update_kwargs(self, X, **model_kwargs): | ||
""" | ||
Update the model argument. | ||
:param X: The input data of the model | ||
:param model_kwargs: The arguments of the model | ||
""" | ||
# Add one feature for padding vector (all 0s) | ||
model_kwargs["input_dim"] = X[1].shape[1] + 1 | ||
return model_kwargs | ||
|
||
def _build_model(self, model_kwargs): | ||
""" | ||
Build the model. | ||
:param model_kwargs: The arguments of the model | ||
""" | ||
if "input_dim" not in model_kwargs: | ||
raise ValueError("Kwarg input_dim cannot be None.") | ||
|
||
cardinality = self.cardinality if self.cardinality > 2 else 1 | ||
bias = False if "bias" not in model_kwargs else model_kwargs["bias"] | ||
|
||
self.sparse_linear = SparseLinear(model_kwargs["input_dim"], cardinality, bias) | ||
|
||
def _calc_logits(self, X, batch_size=None): | ||
""" | ||
Calculate the logits. | ||
:param X: The input data of the model | ||
:param batch_size: The batch size | ||
""" | ||
# Generate sparse multi-modal feature input | ||
F = np.array(list(zip(*X))[1]) + 1 # Correct the index since 0 is the padding | ||
V = np.array(list(zip(*X))[2]) | ||
|
||
outputs = ( | ||
torch.Tensor([]).cuda() | ||
if self.model_kwargs["host_device"] in self.gpu | ||
else torch.Tensor([]) | ||
) | ||
|
||
n = len(F) | ||
if batch_size is None: | ||
batch_size = n | ||
for batch_st in range(0, n, batch_size): | ||
batch_ed = batch_st + batch_size if batch_st + batch_size <= n else n | ||
|
||
features, _ = pad_batch(F[batch_st:batch_ed], 0) | ||
values, _ = pad_batch(V[batch_st:batch_ed], 0, type="float") | ||
|
||
if self.model_kwargs["host_device"] in self.gpu: | ||
features = features.cuda() | ||
values = values.cuda() | ||
|
||
output = self.forward(features, values) | ||
if self.cardinality == 2: | ||
outputs = torch.cat((outputs, output.view(-1)), 0) | ||
else: | ||
outputs = torch.cat((outputs, output), 0) | ||
|
||
return outputs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.