1
$\begingroup$

I have a set of objects, each of which can have (but doesn't always exhibit) a set of properties. Properties are shared between objects, in the sense that different objects can have common properties but the sets of possible properties of any two different objects cannot be identical. The data looks like this:

Property,Object
property1,object1
property2,object1
property2,object2
property3,object1
property3,object3
property4,object2

There are some 1,200 properties and a couple of hundred objects. The number of properties that objects can exhibit is not constant - e.g., object1 has 3 properties in the example above, object 2 has 2 properties, while object 3 has only 1.

I would like to train a ML model that takes a list of observed properties and lists the 10 objects that are the most likely to have been observed, along with some kind of number, expressing the confidence of the recognition.

Problem is, I'm very new to ML and have no idea how to do this.

To begin with, I think I should use a naive Bayes classifier. Since the properties are binary (an object either has a property, or it does not), I think that I should use a Bernoulli naive Bayes classifier. Is this correct?

Then, I have a problem figuring out how to encode the data. Since it is categorical instead of numerical ("property1", etc. instead of a number), I need to encode it somehow. The most obvious way seems to be LabelEncoder - but then the model might attach meaning to how different the numbers are from each other, while in reality the properties are not related this way. It is my understanding that the way to resolve this is by using the OneHotEncoder. Is this correct? And, if I do, given that there are some 1,200 properties, this would result in a huge sparse matrix. Wouldn't that be a problem? Or maybe I should use something else?

Furthermore, I do not understand how to convert this data into something usable by BernoulliNB. It is my understanding that it takes two arguments - an array of vectors, each vector containing the properties of an object, and a vector of objects. I have grouped the data by objects (not sure if I'm doing it properly - I mean, the result seems fine but maybe there is a better way to do it) but BernoulliNB still refuses to accept the data.

from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.naive_bayes import BernoulliNB

data = read_csv('data.csv')
encoded_data = data.copy()

encoder = LabelEncoder()

encoded_data['Property'] = encoder.fit_transform(data['Property'].to_numpy())
encoded_data['Object'] = encoder.fit_transform(data['Object'].to_numpy())

grouped = encoded_data.groupby('Object')

x = []
for name, group in grouped:
    x.append(list(group['Property'].to_numpy()))

y = list(encoded_data['Object'].to_numpy())

model = BernoulliNB()
model.fit(x, y)

I get an error that the input data is in the wrong format. Any suggestions how to do this properly? Do the vectors of the array that is the first argument have to have the same length? If yes, how do I achieve this, given that the different objects can have a different number of properties?

I am stuck at this point but once I get past this it, I have more questions that I don't know how to answer. Once I train the model, I want to use it with future data. Obviously, I need to save the trained model somehow, and then load it, so that I don't need to re-train it every time I run the program. One way to do it is with pickle. What do I save this way? The variable model? Or whatever model.fit(x, y) returns? Or something else?

Again talking about applying the trained model to future data - the future data will again arrive in categorical form, so will need to be encoded too. How do I make sure that it is encoded exactly the same way as the data used to train the model?

Finally, it is my understanding that the model produces a single answer - in my case, the object that is the closest match to the observed properties. I can measure the confidence of this answer. But how do I make it produce a list of the 10 closest matching objects, each with its own confinece measurement?

$\endgroup$
3
  • 3
    $\begingroup$ My feeling is, that you put too much into this one question already. But don't worry, we can cut it into reasonable parts. Let's start with the first thing: Problem formulation. For a classification task, we need to specify 3 things: (a) What is an instance? (b) What is the information of an instance that I know? (c) What is the unknown information, I want to find out? Ignore the encoding for now and just think in information. Given your task, I think that objects are the instances and the set properties of an object are the known information. But what is then the unknown information? $\endgroup$
    – Broele
    Commented Dec 28, 2022 at 15:37
  • 1
    $\begingroup$ Adding to my previous comment: An alternative might be, that it is unknown, what object the properties belong to and the set of properties are known. But then I would have problems to define, what an instance is. We first have to define the Problem on this level, before we can do the next step. $\endgroup$
    – Broele
    Commented Dec 28, 2022 at 15:40
  • $\begingroup$ I'm not sure I understand the question. The known information is what properties each object can (but doesn't always) exhibit. The unknown information is, given a set of observed properties, what are the objects that are the most likely to have been observed. $\endgroup$
    – bontchev
    Commented Dec 29, 2022 at 6:44

2 Answers 2

1
$\begingroup$

To build a machine learning model to classify objects based on their properties. There are a few steps you can follow to achieve this:

  1. Preprocess the data: You can use the LabelEncoder class to convert the categorical property and object names to numerical values. Alternatively, you can then use the OneHotEncoder class to create a binary representation of the properties, with each column corresponding to a different property. This will create a large, sparse matrix, but this is not necessarily a problem for most machine-learning algorithms. Alternatively, you can consult the business team/domain expert (if available) for any higher-level mappings of the available features, so that we can reduce cardinality to some extent.

  2. Split the data into training and testing sets: You can use scikit-learn's train_test_split function to split the data into a training set and a testing set. The training set will be used to train the model, and the testing set will be used to evaluate the model's performance.

  3. Train the model: You can use the BernoulliNB class to train a Bernoulli naive Bayes model on the training data. This model takes a binary array of shape (n_samples, n_features) as input, where each sample represents an object and each feature represents a property. You will need to reshape your data into this format before training the model.

  4. Evaluate the model: You can use scikit-learn's classification_report function to evaluate the model's performance on the testing data. This will give you a measure of the model's accuracy, as well as precision, recall, and f1-score for each class.

  5. Save and load the model: You can use Python's joblib module to save the trained model to a file and load it later.

  6. Use the model on new data: To use the model on new data, you will need to preprocess the data in the same way you did for the training data, using the same LabelEncoder and OneHotEncoder objects. You can then use the predict method of the trained model to classify the new data.

  7. Get the top 10 closest matching objects: To get the top 10 closest matching objects, you can use the predict_proba method of the trained model to get the probability of each object for the given properties. You can then sort the objects by their probabilities and take the top 10.

I suggest to use sklearn Pipelines to setup a robust way doing things.

Hope this helps.

$\endgroup$
2
  • $\begingroup$ Regarding 3, I obviously don't understand what shape the data has to be in, which is partially why I asked my question. Look at my code. BernoulliNB refuses to accept the data I provide to it. What exactly is wrong with the shape of that data and how to fix it? Regarding 5 - the documentation of joblib says it is based on pickle, and I know about that one already. My question was what to save exactly? $\endgroup$
    – bontchev
    Commented Dec 29, 2022 at 6:46
  • $\begingroup$ Regarding 6, again my question was how to ensure that the new data is encoded exactly the same way? Like, if property2 was encoded as 1 in the training data, and the new data contains only it, how to I make sure that it is encoded again as 1 and not as 0, for instance? $\endgroup$
    – bontchev
    Commented Dec 29, 2022 at 6:49
0
$\begingroup$

It would be useful to munge the data into a typical machine learning data organization. The data should be reorganized into a tidy data frame where each individual instance is a row. From your question, it is unclear what an instance is.

Only the features (i.e., 'Property') need to be encoded.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.