def train()

in models.py [0:0]


    def train(self, data, l2=0, init=None, weights=None):
        n = len(data["targets"])
        if weights is None:
            weights = torch.ones(n)
        assert len(weights) == n, "Invalid number of weights"

        # Save for the jacobian:
        self.weights = weights
        self.l2 = n * l2

        X = data["features"]
        y = data["targets"].float()
        theta = torch.randn(X.shape[1], requires_grad=True)
        if init is not None:
            theta.data[:] = init[:]

        crit = torch.nn.BCEWithLogitsLoss(reduction="none")
        optimizer = torch.optim.LBFGS([theta], line_search_fn="strong_wolfe")
        def closure():
            optimizer.zero_grad()
            loss = (crit(X @ theta, y) * weights).sum()
            loss += (self.l2 / 2.0) * (theta**2).sum()
            loss.backward()
            return loss
        for _ in range(100):
            loss = optimizer.step(closure)
        self.theta = theta