123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- import torch as t
- from torch.nn import ReLU, Linear, LazyLinear, Tanh, Sigmoid, Dropout, Sequential
- from federatedml.nn.backend.torch.base import FateTorchLayer
- class InteractiveLayer(t.nn.Module, FateTorchLayer):
- r"""A :class: InteractiveLayer.
- An interface for InteractiveLayer. In interactive layer, the forward method is:
- out = activation( Linear(guest_input) + Linear(host_0_input) + Linear(host_1_input) ..)
- Args:
- out_dim: int, the output dimension of InteractiveLayer
- host_num: int, specify the number of host party, default is 1, need to modify this parameter
- when running multi-party modeling
- guest_dim: int or None, the input dimension of guest features, if None, will use LazyLinear layer
- that automatically infers the input dimension
- host_dim: int, or None:
- int: the input dimension of all host features
- None: automatically infer the input dimension of all host features
- activation: str, support relu, tanh, sigmoid
- dropout: float in 0-1, if None, dropout is disabled
- guest_bias: bias for guest linear layer
- host_bias: bias for host linear layers
- need_guest: if false, will ignore the input of guest bottom model
- """
- def __init__(
- self,
- out_dim,
- guest_dim=None,
- host_num=1,
- host_dim=None,
- activation='relu',
- dropout=None,
- guest_bias=True,
- host_bias=True,
- need_guest=True,
- ):
- t.nn.Module.__init__(self)
- FateTorchLayer.__init__(self)
- self.activation = None
- if activation is not None:
- if activation.lower() == 'relu':
- self.activation = ReLU()
- elif activation.lower() == 'tanh':
- self.activation = Tanh()
- elif activation.lower() == 'sigmoid':
- self.activation = Sigmoid()
- else:
- raise ValueError(
- 'activation not support {}, avail: relu, tanh, sigmoid'.format(activation))
- self.dropout = None
- if dropout is not None:
- assert isinstance(dropout, float), 'dropout must be a float'
- self.dropout = Dropout(p=dropout)
- assert isinstance(out_dim, int), 'out_dim must be an int >= 0'
- self.param_dict['out_dim'] = out_dim
- self.param_dict['activation'] = activation
- self.param_dict['dropout'] = dropout
- self.param_dict['need_guest'] = need_guest
- assert isinstance(
- host_num, int) and host_num >= 1, 'host number is an int >= 1'
- self.param_dict['host_num'] = host_num
- if guest_dim is not None:
- assert isinstance(guest_dim, int)
- if host_dim is not None:
- assert isinstance(host_dim, int)
- self.guest_bias = guest_bias
- self.param_dict['guest_dim'] = guest_dim
- self.param_dict['host_dim'] = host_dim
- self.param_dict['guest_bias'] = guest_bias
- self.param_dict['host_bias'] = host_bias
- if need_guest:
- if guest_dim is None:
- self.guest_model = LazyLinear(out_dim, guest_bias)
- else:
- self.guest_model = Linear(guest_dim, out_dim, guest_bias)
- else:
- self.guest_model = None
- self.out_dim = out_dim
- self.host_dim = host_dim
- self.host_bias = host_bias
- self.host_model = None
- self.need_guest = need_guest
- self.host_model = t.nn.ModuleList()
- for i in range(host_num):
- self.host_model.append(self.make_host_model())
- if self.dropout is not None:
- self.act_seq = Sequential(
- self.activation,
- self.dropout
- )
- else:
- self.act_seq = Sequential(
- self.activation
- )
- def lazy_to_linear(self, guest_dim=None, host_dims=None):
- if isinstance(
- self.guest_model,
- t.nn.LazyLinear) and guest_dim is not None:
- self.guest_model = t.nn.Linear(
- guest_dim, self.out_dim, bias=self.guest_bias)
- if isinstance(
- self.host_model[0],
- t.nn.LazyLinear) and host_dims is not None:
- new_model_list = t.nn.ModuleList()
- for dim in host_dims:
- new_model_list.append(
- t.nn.Linear(
- dim,
- self.out_dim,
- bias=self.host_bias))
- self.host_model = new_model_list
- def make_host_model(self):
- if self.host_dim is None:
- return LazyLinear(self.out_dim, self.host_bias)
- else:
- return Linear(self.host_dim, self.out_dim, self.host_bias)
- def forward(self, x_guest, x_host):
- if self.need_guest:
- g_out = self.guest_model(x_guest)
- else:
- g_out = 0
- h_out = None
- if isinstance(x_host, list):
- for m, data in zip(self.host_model, x_host):
- out_ = m(data)
- if h_out is None:
- h_out = out_
- else:
- h_out += out_
- else:
- h_out = self.host_model[0](x_host)
- return self.activation(g_out + h_out)
|