Hey everyone, I have a code in python that creates and train a TabTransformer model that I, then, extract its embeddings so I can use it to generate embedding vectors for tabular data that I have.
Here is the code:
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Define the TabTransformer model
class TabTransformer(nn.Module):
def __init__(self, num_features, num_classes, dim_embedding=64, num_heads=4, num_layers=4):
super(TabTransformer, self).__init__()
self.embedding = nn.Linear(num_features, dim_embedding)
encoder_layer = nn.TransformerEncoderLayer(d_model=dim_embedding, nhead=num_heads, batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.classifier = nn.Linear(dim_embedding, num_classes)
def forward(self, x):
x = self.embedding(x)
x = x.unsqueeze(1) # Adding a sequence length dimension
x = self.transformer(x)
x = torch.mean(x, dim=1) # Pooling
x = self.classifier(x)
return x
data = pd.DataFrame({
"customer_id": [1, 2, 3, 4, 5],
"product_category": [1, 2, 1, 3, 2],
"ammount": [100, 200, 150, 300, 250]
})
X = data.drop("customer_id", axis = 1)
y = data["customer_id"] - 1
# Splitting the dataset into training and test sets
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.0, random_state=42)
X_train = X
X_test = X
y_train = y
y_test = y
# Standardizing the features
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Model parameters
num_features = X_train_scaled.shape[1]
num_classes = 5 # Adjusted based on unique customer ids
# Initialize the model, loss, and optimizer
model = TabTransformer(num_features, num_classes).to(torch.device('cpu'))
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# Converting data to tensors
X_train_tensor = torch.FloatTensor(X_train_scaled)
y_train_tensor = torch.LongTensor(y_train.values)
# Training loop
for epoch in range(100):
optimizer.zero_grad()
output = model(X_train_tensor)
loss = criterion(output, y_train_tensor)
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f'Epoch {epoch}, Loss: {loss.item()}')
# Evaluation
model.eval()
X_test_tensor = torch.FloatTensor(X_test_scaled)
y_test_tensor = torch.LongTensor(y_test.values)
with torch.no_grad():
predictions = model(X_test_tensor)
_, predicted_classes = torch.max(predictions, 1)
accuracy = (predicted_classes == y_test_tensor).float().mean()
print(f'Test Accuracy: {accuracy.item()}')
# Get embedding from test data
embedding = model.embedding
embedding(X_test_tensor)
This works great, but now I want to convert this code to Elixir so I can use it in my backend to serve embeddings without needing to connect to some python code/service.
I’m very new to the ML world, so I’m sincerely not sure how I should start with doing that, especially the part that actually creates the model itself.
Update
So, I started trying to convert it, so far I was able to generate the inputs and the scaler, but I’m totally stuck in the model creation, I have no idea what are the equivalent functions in Axon for the ones used in the TabTransformer class.
Mix.install([
{:kino_explorer, "~> 0.1.20"},
{:axon, "~> 0.7"},
{:scholar, github: "elixir-nx/scholar"},
{:table_rex, "~> 4.0", override: true}
])
alias Explorer.{Series, DataFrame}
alias Scholar.Preprocessing.StandardScaler
require DataFrame
require Series
data = DataFrame.new(
customer_id: [1, 2, 3, 4, 5],
product_category: [1, 2, 1, 3, 2],
amount: [100, 200, 150, 300, 250]
)
x = DataFrame.discard(data, :customer_id)
y =
data
|> DataFrame.select(:customer_id)
|> DataFrame.mutate(customer_id: customer_id - 1)
x_train = Nx.stack(x, axis: 1)
y_train = Nx.stack(y, axis: 1)
x_test = Nx.stack(x, axis: 1)
y_test = Nx.stack(y, axis: 1)
scaler = StandardScaler.fit(x_train, axes: [0])
x_train_scaled = StandardScaler.transform(scaler, x_train)
x_test_scaled = StandardScaler.transform(scaler, x_test)
{_, num_features} = Nx.shape(x_train_scaled)
# Adjusted based on unique customer ids
num_classes = 5