changing value of an instance of a class from another class - python

I have training and test dataset as Pytorch Tensor objects and I want to change their values from another class, is this somehow possible? The code below does not change their values:
class BO:
def __init__(self):
self.data = DataCollector().df_subset
self.risk = DataCollector().risk_subset.unique(dim=0)
self.device = torch.device('cpu')
self.X_init = None
self.outs_init = torch.zeros(0)
self.y_init = []
if torch.cuda.is_available():
self.device = torch.device('cuda:7')
def create_init_X_Y(self):
'''
Creates the initial X and y values.
'''
model.TrainDataset().df = self.data
_, loss = model.train()
I need to overwrite the value of model.TrainDataset().df with the value of self.data.

Related

Passing one variable from one class to another

i am take the no. of iteration in for loop running from a different class.
for e in range(1,EPOCHS+1 ):
g=0
for j in range (jin):
g=g+1
print(g)
train_epoch_loss = 0
train_epoch_acc = 0
p1=cross(g)
#pw=pq.save(g)
#p1=pq.get()
model.train()
and saving it in this class and need to use that value.
class cross():
c=0
def init__ (self,value):
self.v = value
print('i entered the classs')
for train_index, test_index in kf.split(x_trainval):
c=c+1
# e1=num()
x_train,x_val=x[train_index],x[test_index]
y_train,y_val=y[train_index],y[test_index]
print("TRAIN:", train_index, "TEST:", test_index)
print("epoch",c)
# print(e1.__getitem__())
if (v == c):
print("compare")
break
train_dataset = classifierdataset(torch.from_numpy(x_train).float(), torch.from_numpy(y_train).long())
val_dataset = classifierdataset(torch.from_numpy(x_val).float(), torch.from_numpy(y_val).long())
test_dataset = classifierdataset(torch.from_numpy(x_test).float(), torch.from_numpy(y_test).long())
train_loader = DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE)
val_loader = DataLoader(dataset = val_dataset, batch_size = 1)
test_loader = DataLoader(dataset = test_dataset , batch_size = 1)
print("train",x_train,"val",x_val)
Now the issues i am having is how to use the set value in the same class.
just put a return statement and take the values accordingly to the return statement.
I think you are asking if you can access obj.value within other methods or self.value within the same class methods .
Maybe this will help you ,
https://medium.com/python-features/class-vs-instance-variables-8d452e9abcbd#:~:text=Class%20variables%20are%20shared%20across,surprising%20behaviour%20in%20our%20code.
Accessing an instance variable within an instance method (a method that accepts self as first argument) -
Correct way -
class Student:
# constructor
def __init__(self, name, age):
# Instance variable
self.name = name
self.age = age
def show(self):
print('Name:', self.name, 'Age:', self.age)
Wrong way -
class Student:
# constructor
def __init__(self, name, age):
# Instance variable
self.name = name
self.age = age
# instance method access instance variable
def show(self):
print('Name:', name, 'Age:', age)
student_obj.show()
gives
NameError: name 'name' is not defined
within some other method you need to have the obj available :
c_obj = cross(5)
def any_other_method(c: cross) -> None:
print("value is ", c.v)
any_other_method(c_obj) prints "value is 5"

Trouble with minimal hvp on pytorch model

While autograd's hvp tool seems to work very well for functions, once a model becomes involved, Hessian-vector products seem to go to 0. Some code.
First, I define the world's simplest model:
class SimpleMLP(nn.Module):
def __init__(self, in_dim, out_dim):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(in_dim, out_dim),
)
def forward(self, x):
'''Forward pass'''
return self.layers(x)
Then, a loss function:
def objective(x):
return torch.sum(0.25 * torch.sum(x)**4)
We instantiate it:
Arows = 2
Acols = 2
mlp = SimpleMLP(Arows, Acols)
Finally, I'm going to define a "forward" function (distinct from the model's forward function) that will serve as the the full model+loss that we want to analyze:
def forward(*params_list):
for param_val, model_param in zip(params_list, mlp.parameters()):
model_param.data = param_val
x = torch.ones((Arows,))
return objective(mlp(x))
This passes a ones vector into the single-layer "mlp," and passes it into our quadratic loss.
Now, I attempt to compute:
v = torch.ones((6,))
v_tensors = []
idx = 0
#this code "reshapes" the v vector as needed
for i, param in enumerate(mlp.parameters()):
numel = param.numel()
v_tensors.append(torch.reshape(torch.tensor(v[idx:idx+numel]), param.shape))
idx += numel
And finally:
param_tensors = tuple(mlp.parameters())
reshaped_v = tuple(v_tensors)
soln = torch.autograd.functional.hvp(forward, param_tensors, v=reshaped_v)
But, alas, the Hessian-Vector Product in soln is all 0's. What is happening?
What's happening is that strict is False by default in the hvp() function and a tensor of 0's is returned as the Hessian Vector Product instead of an error (source).
If you try with strict=True, an error RuntimeError: The output of the user-provided function is independent of input 0. This is not allowed in strict mode. is returned instead. And when I looked at the full error, I suspect that this error comes from _check_requires_grad(jac, "jacobian", strict=strict) which indicates that the jacobian jac is None.
Update:
Following is a full working example:
import torch
from torch import nn
# your loss function
def objective(x):
return torch.sum(0.25 * torch.sum(x)**4)
# Following are utilities to make nn.Module functional
# borrowed from the link I posted in comment
def del_attr(obj, names):
if len(names) == 1:
delattr(obj, names[0])
else:
del_attr(getattr(obj, names[0]), names[1:])
def set_attr(obj, names, val):
if len(names) == 1:
setattr(obj, names[0], val)
else:
set_attr(getattr(obj, names[0]), names[1:], val)
def make_functional(mod):
orig_params = tuple(mod.parameters())
# Remove all the parameters in the model
names = []
for name, p in list(mod.named_parameters()):
del_attr(mod, name.split("."))
names.append(name)
return orig_params, names
def load_weights(mod, names, params):
for name, p in zip(names, params):
set_attr(mod, name.split("."), p)
# your forward function with update
def forward(*new_params):
# this line replace your for loop
load_weights(mlp, names, new_params)
x = torch.ones((Arows,))
out = mlp(x)
loss = objective(out)
return loss
# your simple MLP model
class SimpleMLP(nn.Module):
def __init__(self, in_dim, out_dim):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(in_dim, out_dim),
)
def forward(self, x):
'''Forward pass'''
return self.layers(x)
if __name__ == '__main__':
# your model instantiation
Arows = 2
Acols = 2
mlp = SimpleMLP(Arows, Acols)
# your vector computation
v = torch.ones((6,))
v_tensors = []
idx = 0
#this code "reshapes" the v vector as needed
for i, param in enumerate(mlp.parameters()):
numel = param.numel()
v_tensors.append(torch.reshape(torch.tensor(v[idx:idx+numel]), param.shape))
idx += numel
reshaped_v = tuple(v_tensors)
#make model's parameters functional
params, names = make_functional(mlp)
params = tuple(p.detach().requires_grad_() for p in params)
#compute hvp
soln = torch.autograd.functional.vhp(forward, params, reshaped_v, strict=True)
print(soln)
Did you try it with doubles instead of floats? I did some tests on my own that showed fairly large error when backproping with 32 bit float (on the order of 1e-5) compared to doubles.

Cannot innitialise superclass from Ray actor process

I am trying to parallelise a sampling process, so I created a Sampler object. The Sampler depends on two datasets, which are large (stored as numpy arrays), which are arguments to the constructor. To avoid having duplicates in the object store, my idea has been to first use ray.put to add the object to the object store and then initialise the Sampler objects with the corresponding ids.
Moreover, I don't want to add the decorators to the Sampler class. Instead, I created a subclass of Sampler, RemoteSampler which decorates the methods of the superclass and modifies them by adding the .remote() call. However, I seem to be unable to initialise the superclass from the ActorClass. I get a type error:
TypeError: super() argument 1 must be type, not ActorClass(RemoteSampler).
Skeleton code below:
import ray
import numpy as np
class Sampler(object):
def __init__(self, train_data, d_train_data, *others):
# these can be big, so we want to have only one copy that
# mutliple actors share
if isinstance(train_data, np.ndarray):
self.train_data = train_data
else:
self.train_data = ray.get(train_data)
if isinstance(d_train_data, np.ndarray):
self.d_train_data = d_train_data
else:
self.d_train_data = ray.get(d_train_data)
# Initialise the rest of the sampler state
self.d1 = {}
self.d2 = {}
def __call__(self, features, n_samples):
a, b, c = self._sampling_loop(features, n_samples)
# process a, b, c and return something
return a, b, c, features
def build_lookups(self, X):
self.d1 = {0: X[0]}
self.d2 = {1: X[1]}
return self.d1, self.d2
def _sampling_loop(self, features, n_samples):
# Use train_data, d_train data and other attributes return some data to call
return 0, 0, 0
#ray.remote
class RemoteSampler(Sampler):
def __init__(self, *args):
super(RemoteSampler, self).__init__(*args)
self.__call__ = ray.method(self.__call__, num_return_vals=4)
self.build_lookups = ray.method(self.build_lookups, num_return_vals=2)
def __call__(self, anchor, num_samples):
return self.__call__(anchor, num_samples).remote()
def build_lookups(self, X):
a, b, c = self.build_lookups.remote(X)
return a, b, c
def _fit_parallel(*args):
# method of a class where the RemoteSampler objects are initialised
# copy large objects to object store
train_data, d_train_data, *others = args
train_data_id = ray.put(train_data)
d_train_data_id = ray.put(d_train_data)
n_args = (train_data_id, d_train_data_id, *others)
return [RemoteSampler.remote(*n_args) for _ in range(4)]

How to store a variable of an instance of a class in an instance of another class

I have two self-defined classes, one is a child of the gurobipy-class and is supposed to make a lp-model. The other one I made to store variables. Now I want to store some variables of the model class in the variables class.
Here are my classes:
class Model(gb.Model):
def __init__(self):
super().__init__()
def create_model(self, var):
dim = var.dimensions()
# variables
x = self.addVars(dim[0], dim[1], vtype=gb.GRB.BINARY, name="x")
D_l = self.addVars(dim[1], lb=0, name='D_l')
D_max = self.addVar(lb=0, name='D_max')
# objective
self.setObjective(D_max, gb.GRB.MINIMIZE)
# constraints
self.addConstrs((x.sum(i, '*') == 1 for i in range(dim[0])), name="b")
self.addConstrs((D_max >= D_l[l] for l in range(dim[1])), name="c")
self.addConstrs((D_l[l] >= var.dist_mat()[i, j] * (x[i, l] + x[j, l] - 1) for i in range(dim[0])
for j in range(dim[0]) for l in range(dim[1])), name='a')
self.update()
class Variables:
def __init__(self, data, number_of_clusters, neighbourhood_size):
self.data = data
self.number_of_clusters = number_of_clusters
self.neighbourhood_size = neighbourhood_size
self.variables_before = None
self.variables_now = None
self.ofv_before = None
self.ofv_now = None
self.x = None
def dist_mat(self):
from scipy.spatial import distance_matrix
return distance_matrix(self.data, self.data)
def dimensions(self):
from numpy import shape
data_objects = shape(self.data)[0]
number_of_clusters = self.number_of_clusters
return data_objects, number_of_clusters
def print_dist_mat(self):
print(self.dist_mat())
It's the x-variable I want to store. First, I tried to store it in the instance of the Model-class. I added to the init-function this line self.x = None. But it raise an AttributeError: 'x' is not a model attribute. I guess, this is because the gurobipy-class doesn't have a x attribute.
Next, I wanted to store it in an instance of the variable-class. I wanted to write a function in the model class, which should do the trick. This is the function:
def store_x(self, var):
var.x = self.x
Then, I got this error: gurobipy.GurobiError: Unable to retrieve attribute 'x', I can't understand why.
I can't even access the x-variable from outside the function. I can print it from inside the function, but nothing more. The problem is, I need this x-variable in a later stage.
How can I achieve this? How can I store the x-variable to access it at a later point? It doesn't have to be in the variable-class, any other solution is appreciated as well.
Ok first off I see an issue with Your code:
def store_x(self, var):
var.x = self.x
It Needs to be changed to :
def store_x(self, var):
self.x = var.x
This is because whatever you send in the 'var' parameter will only be a copy of whatever you actually passed. And then its scope will only last to the end of that store_x method. So instead you pass that copy and tell your variable class instance to store it inside it's x value.
As for the error you got with:
self.x = None # inside your Model class
I'm not sure why, as I tried the following and it runs fine:
class Variables:
def __init__(self):
self.data = data
self.number_of_clusters = number_of_clusters
self.neighbourhood_size = neighbourhood_size
self.variables_before = None
self.variables_now = None
self.ofv_before = None
self.ofv_now = None
self.x = None
So I'm updating my answer with a deeper example after getting clarification on what is needed. Here are two skeleton classes named 'Variables', 'Model', respectivly:
class Variables:
def __init__(self):
self.data = None
self.number_of_clusters = None
self.neighbourhood_size = None
self.variables_before = None
self.variables_now = None
self.ofv_before = None
self.ofv_now = None
self.x = None
def get_x(self,modelx):
self.x = modelx
class Model:
def __init__(self):
self.x = ({}, {})
# create your class instances here
newVar = Variables()
newModel = Model()
# one way to assign your Variable class's x attribute the tuple dict in question.
newVar.x = newModel.x
# alternate way is to create a function inside your Variable class that updates the x variable based on the argument you send it.
newVar.get_x(newModel.x)

Any fast method to cast a numpy array to a custom class?

I have a numpy array of shape (1000, 20) where a row is has 19 float values (feature) and one last value is a binary value (label).
The custom class is:
class Point(object):
feature = None
label = None
def __init__(self, feature, label):
self.feature = feature
self.label = label
def __str__(self):
return "{0} {1} labelled {2}".format(self.__class__.__name__, self.feature, self.label)
def __repr__(self):
return "{0}({1}, {2})".format(self.__class__.__name__, self.feature, self.label)
Is there a way can just pass the matrix and all rows can be converted to Point object? Or should I just iterate through each row?
For iterative solution I used this function:
def to_data_point(matrix):
new_data = []
for row in matrix:
feature = row[:-1]
label = row[-1]
new_data.append(Point(feature, label))
return new_data

Categories

Resources