h2oai / h2o-3

H2O is an Open Source, Distributed, Fast & Scalable Machine Learning Platform: Deep Learning, Gradient Boosting (GBM) & XGBoost, Random Forest, Generalized Linear Modeling (GLM with Elastic Net), K-Means, PCA, Generalized Additive Models (GAM), RuleFit, Support Vector Machine (SVM), Stacked Ensembles, Automatic Machine Learning (AutoML), etc.
http://h2o.ai
Apache License 2.0
6.87k stars 1.99k forks source link

Create a pytorch model from my H2O model #16400

Open matt7salomon opened 3 days ago

matt7salomon commented 3 days ago

My organization only allows pytroch models to go into production. I wrote the following code to convert my trained h2o model into pytorch but it errors out.

import torch
import torch.nn as nn

# Define a PyTorch model with the same architecture as the H2O model
class ConvertedH2OModel(nn.Module):
    def __init__(self, input_dim):
        super(ConvertedH2OModel, self).__init__()
        self.fc1 = nn.Linear(input_dim, 256)
        self.bn1 = nn.BatchNorm1d(256)
        self.fc2 = nn.Linear(256, 128)
        self.bn2 = nn.BatchNorm1d(128)
        self.fc3 = nn.Linear(128, 64)
        self.fc4 = nn.Linear(64, 1)
        self.dropout = nn.Dropout(p=0.3)

    def forward(self, x):
        x = torch.relu(self.bn1(self.fc1(x)))
        x = self.dropout(x)
        x = torch.relu(self.bn2(self.fc2(x)))
        x = self.dropout(x)
        x = torch.relu(self.fc3(x))
        x = self.fc4(x)
        return x

# Initialize the PyTorch model
input_dim = num_features  # Assuming num_features is defined
torch_model = ConvertedH2OModel(input_dim)

# Check if the model has weights and biases
if specific_model._model_json["output"]["weights"] is not None and specific_model._model_json["output"]["biases"] is not None:
    # Extract weights and biases from the H2O model
    h2o_weights = [specific_model.weights(i) for i in range(len(specific_model.params['hidden']))]
    h2o_biases = [specific_model.biases(i) for i in range(len(specific_model.params['hidden']))]

    # Assign the extracted weights and biases to the PyTorch model
    with torch.no_grad():
        torch_model.fc1.weight = nn.Parameter(torch.tensor(h2o_weights[0].T, dtype=torch.float32))
        torch_model.fc1.bias = nn.Parameter(torch.tensor(h2o_biases[0], dtype=torch.float32))
        torch_model.fc2.weight = nn.Parameter(torch.tensor(h2o_weights[1].T, dtype=torch.float32))
        torch_model.fc2.bias = nn.Parameter(torch.tensor(h2o_biases[1], dtype=torch.float32))
        torch_model.fc3.weight = nn.Parameter(torch.tensor(h2o_weights[2].T, dtype=torch.float32))
        torch_model.fc3.bias = nn.Parameter(torch.tensor(h2o_biases[2], dtype=torch.float32))
        torch_model.fc4.weight = nn.Parameter(torch.tensor(h2o_weights[3].T, dtype=torch.float32))
        torch_model.fc4.bias = nn.Parameter(torch.tensor(h2o_biases[3], dtype=torch.float32))

    # Now the torch_model is initialized with the weights and biases from the H2O model
else:
    print("The model does not have weights and biases available for extraction.")

Here is my h2o mode summary:

DeepLearning_1_AutoML_1_20240923_211230
Status of Neuron Layers: predicting target, regression, gaussian distribution, Quadratic loss, 3,101 weights/biases, 77.1 KB, 104,018 training samples, mini-batch size 1
    layer    units    type       dropout    l1    l2    mean_rate               rate_rms                momentum    mean_weight             weight_rms           mean_bias             bias_rms
--  -------  -------  ---------  ---------  ----  ----  ----------------------  ----------------------  ----------  ----------------------  -------------------  --------------------  -----------------------
    1        286      Input      0.0
    2        10       Rectifier  0.0        0.0   0.0   0.025230179532672037    0.009651198983192444    0.0         0.00023786274331370066  0.0954655110836029   0.7788656104215739    0.5845489501953125
    3        10       Rectifier  0.0        0.0   0.0   0.008876945667725521    0.008416924625635147    0.0         -0.004443328110501171   0.28273701667785645  0.9809969561491746    0.06552445888519287
    4        10       Rectifier  0.0        0.0   0.0   0.011014057626889553    0.023908764123916626    0.0         0.02570307268295437     0.3091564178466797   1.022937802614036     0.04745239019393921
    5        1        Linear                0.0   0.0   0.00039743693923810497  0.00022761948639526963  0.0         0.034631247818470004    0.31644153594970703  0.017902538961798036  1.0971281125650402e-154
{'default': 'Automatic', 'actual': 'Automatic', 'input': 'Automatic'}
wendycwong commented 3 days ago

@matt7salomon : You can use our DAI tools which will allow you to generate pytorch models: https://www.google.com/search?client=safari&rls=en&q=h2o+Driverless+AI&ie=UTF-8&oe=UTF-8 . Currently, we don't support pytorch on our opensource platform.

matt7salomon commented 3 days ago

Thanks but my code seem to be able to get the structure transferred from h2o to pytroch and not the weights and biases but i dont need those. I am trying to train in torch again using the same structure and same parameters. I should be able to get all the model info from h2o connection.

wendycwong commented 2 days ago

@matt7salomon : Sorry I misunderstood your question.

To get the weight and biases exposed to the returned model, you need to set the parameter export_weights_and_biases=True like this:

dlmodel = H2ODeepLearningEstimator(hidden=[17,191], epochs=1, balance_classes=False, reproducible=True, seed=1234, export_weights_and_biases=True)

The weights and biases will come back as H2O frames. Here is an example of this:

==================================================================== covtype = h2o.upload_file(pyunit_utils.locate("smalldata/covtype/covtype.20k.data")) covtype[54] = covtype[54].asfactor()

dlmodel = H2ODeepLearningEstimator(hidden=[17,191], epochs=1, balance_classes=False, reproducible=True, seed=1234, export_weights_and_biases=True) dlmodel.train(x=list(range(54)),y=54,training_frame=covtype) print(dlmodel)

weights1 = dlmodel.weights(0) weights2 = dlmodel.weights(1) weights3 = dlmodel.weights(2)

biases1 = dlmodel.biases(0) biases2 = dlmodel.biases(1) biases3 = dlmodel.biases(2)

w1c = weights1.ncol w1r = weights1.nrow assert w1c == 52, "wrong dimensionality! expected {0}, but got {1}.".format(52, w1c) assert w1r == 17, "wrong dimensionality! expected {0}, but got {1}.".format(17, w1r)

w2c = weights2.ncol w2r = weights2.nrow assert w2c == 17, "wrong dimensionality! expected {0}, but got {1}.".format(17, w2c) assert w2r == 191, "wrong dimensionality! expected {0}, but got {1}.".format(191, w2r)

w3c = weights3.ncol w3r = weights3.nrow assert w3c == 191, "wrong dimensionality! expected {0}, but got {1}.".format(191, w3c) assert w3r == 7, "wrong dimensionality! expected {0}, but got {1}.".format(7, w3r)

b1c = biases1.ncol b1r = biases1.nrow assert b1c == 1, "wrong dimensionality! expected {0}, but got {1}.".format(1, b1c) assert b1r == 17, "wrong dimensionality! expected {0}, but got {1}.".format(17, b1r)

b2c = biases2.ncol b2r = biases2.nrow assert b2c == 1, "wrong dimensionality! expected {0}, but got {1}.".format(1, b2c) assert b2r == 191, "wrong dimensionality! expected {0}, but got {1}.".format(191, b2r)

b3c = biases3.ncol b3r = biases3.nrow assert b3c == 1, "wrong dimensionality! expected {0}, but got {1}.".format(1, b3c) assert b3r == 7, "wrong dimensionality! expected {0}, but got {1}.".format(7, b3r)

wendycwong commented 2 days ago

If you already have your model trained without setting that parameter, you can still get the weights and biases via mojo. Here is an example of how to do that:

assumed that you have model:

=========================== model_mojo = model.download_mojo("/some/directory/forMojo")

The model_mojo will be a zip file. Unzip the file and you will find the weights and biases information in the file model.ini.

Here is an example of my model.ini

image