I'm trying to use a pipeline with an RBM and a MLPclassifier, my input data will pass first on the rbm, a dimensiality reduction will be made (from 513 features to 100 features (nodes)), I managed to write the code and it seems to be correct but i get this error at the end
UndefinedMetricWarning: Precision and F-score are ill-defined and
being set to 0.0 in labels with no predicted samples. 'precision',
'predicted', average, warn_for)
precision recall f1-score support
0 0.00 0.00 0.00 25
1 0.00 0.00 0.00 28
2 0.00 0.00 0.00 28
3 0.00 0.00 0.00 34
4 0.00 0.00 0.00 25
avg / total 0.00 0.00 0.00 140
Here's my code
X_train, X_test, Y_train, Y_test = train_test_split(X,
Y,test_size=0.2,random_state=0)
mlp = MLPClassifier(hidden_layer_sizes=100,activation="tanh",max_iter=200)
rbm = BernoulliRBM(random_state=0, verbose=True)
classifier = Pipeline(steps=[('rbm', rbm), ('mlpclassifier', mlp)])
rbm.learning_rate = 0.06
rbm.n_iter = 20
rbm.n_components = 100
classifier.fit(X_train, Y_train)
print("MLP using RBM features:\n%s\n" % (metrics.classification_report(Y_test,
classifier.predict(X_test))))
Thanks for your answer Kumar, i tried to take one sample from the test set, and make a prediction
print('the real label', Y_train[0])
print('the prediction', classifier.predict(X_train[0].reshape(1,-1)))
And that's what i get as an output
the real label [1 0 0 0 0]
the prediction [[0 0 0 0 0]]
It seems to me that the classifier(Pipeline) is not trained !!
Related
My goal is to predict the count (variable y) based on various features (variable x). My y is most of the time (98.4%) equal to 0, so this data is inflated by 0.
Based on this premise, I thought that using the Zero Inflated Model with SVC could be an asset, given the characteristics of my data.
So I found a code on the internet and i'm trying to apply it to my problem (i'm very new to this)
from sklego.meta import ZeroInflatedRegressor
zir = ZeroInflatedRegressor(
classifier=SVC(),
regressor=LinearRegression()
)
zir.fit(scaled_train, y_train.values.ravel())
predictions = zir.predict(scaled_test)
I believe my problem is exactly in this part of the code below, where I am appending only 0s and 1s. However, how do I make predictions for the other counts? This is what I'm not able to understand.
trashold = .5
preds =[]
for val in predictions:
if val>trashold:
preds.append(1)
else:
preds.append(0)
accuracy_score(y_test,preds)
0.9976208095270875
Even in my classification_report I have an alert precisely because I don't have samples for the other labels. How to fix such a problem?
print(classification_report(y_test,preds))
Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.
precision recall f1-score support
0.0 1.00 1.00 1.00 916493
1.0 0.86 0.97 0.91 11398
2.0 0.00 0.00 0.00 1498
3.0 0.00 0.00 0.00 231
4.0 0.00 0.00 0.00 73
5.0 0.00 0.00 0.00 22
6.0 0.00 0.00 0.00 5
7.0 0.00 0.00 0.00 5
8.0 0.00 0.00 0.00 2
9.0 0.00 0.00 0.00 1
accuracy 1.00 929728
macro avg 0.19 0.20 0.19 929728
weighted avg 1.00 1.00 1.00 929728
To illustrate my problem, I have the following image. Which shows that 0 values are reasonably well predicted, but from 1 I don't have any predictions anymore.
plt.clf()
plt.hist([y_train.values, preds], log=True)
plt.legend(('orig','pred'))
plt.show()
Any idea how I can improve this model and have predictions for all classes? Thank you very much in advance.
EDIT 1 - Based on Alex Comments
I tried to implement Alex's suggestions on a subset of the data.
df_count = df.groupby(['count']).size().to_frame(name = 'size').reset_index()
df_count
count size
0.0 859225
1.0 9208
2.0 1224
3.0 180
4.0 59
5.0 3
6.0 3
7.0 2
8.0 1
zir = ZeroInflatedRegressor(
classifier=SVC(),
regressor=LinearRegression()
)
zir.fit(scaled_train, y_train.values.ravel())
predictions = zir.predict(scaled_test)
As indicated, I switched the assessment measure to the MSE
mean_squared_error(y_test['count'].values,predictions)
0.0033405170885248804
I made a new plot of this data subset, which demonstrates that the model has some success in predicting when there are counts of up to 3. for counts greater than 3, as the sample is very small, the model has difficulty in making predictions.
plt.clf()
plt.hist([y_test['count'].values, predictions], log=True)
plt.legend(('real','predction'))
plt.show()
In Scikit learn, when doing X,Y = make_moons(500,noise = 0.2) and after printing X and Y, I see that they are like arrays with a bunch of entries but with no commas?
I have data that I want to use instead of the Scikit learn moons dataset, but I dont understand what data type these Scikit learn data sets are and how I can make my data follow this data type.
The first one X is a 2d array:
array([[-6.72300890e-01, 7.40277997e-01],
[ 9.60230259e-02, 9.95379113e-01],
[ 3.20515776e-02, 9.99486216e-01],
[ 8.71318704e-01, 4.90717552e-01],
....
[ 1.61911895e-01, -4.55349012e-02]])
Which contains the x-axis, and y-axis position of points.
The second part of the tuple: y, is an array that contains the labels (0 or 1 for binary classification).
array([0, 0, 0, 0, 1, ... ])
To use this data in a simple classification task, you could do the following:
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
# Create dataset
X, y = make_moons(500,noise = 0.2)
# Split dataset in a train part and a test part
train_X, test_X, train_y, test_y = train_test_split(X, y)
# Create the Logistic Regression classifier
log_reg = LogisticRegression()
# Fit the logistic regression classifier
log_reg.fit(train_X, train_y)
# Use the trained model to predit con the train and predict samples
train_y_pred = log_reg.predict(train_X)
test_y_pred = log_reg.predict(test_X)
# Print classification report on the training data
print(classification_report(train_y, train_y_pred))
# Print classification report on the test data
print(classification_report(test_y, test_y_pred))
The results are:
On training data
precision recall f1-score support
0 0.88 0.87 0.88 193
1 0.86 0.88 0.87 182
accuracy 0.87 375
macro avg 0.87 0.87 0.87 375
weighted avg 0.87 0.87 0.87 375
On test data
precision recall f1-score support
0 0.81 0.89 0.85 57
1 0.90 0.82 0.86 68
accuracy 0.86 125
macro avg 0.86 0.86 0.86 125
weighted avg 0.86 0.86 0.86 125
As we can see, the f1_score is not very different between the train and the test set, the model is not overfitting.
I wanted to leverage this answer How to plot scikit learn classification report? turning an sklearn classification report into a heatmap.
It's all working with their sample report, however my classification report looks slightly different and is thus screwing up the functions.
Their report (notice the avg / total):
sampleClassificationReport =
precision recall f1-score support
Acacia 0.62 1.00 0.76 66
Blossom 0.93 0.93 0.93 40
Camellia 0.59 0.97 0.73 67
Daisy 0.47 0.92 0.62 272
Echium 1.00 0.16 0.28 413
avg / total 0.77 0.57 0.49 858
My report with metrics.classification_report(valid_y, y_pred) :
precision recall f1-score support
0 1.00 0.18 0.31 11
1 0.00 0.00 0.00 14
2 0.00 0.00 0.00 19
3 0.50 0.77 0.61 66
4 0.39 0.64 0.49 47
5 0.00 0.00 0.00 23
accuracy 0.46 180
macro avg 0.32 0.27 0.23 180
weighted avg 0.35 0.46 0.37 180
The issue, from the selected answer in the heatmap link, is here:
for line in lines[2 : (len(lines) - 2)]:
t = line.strip().split()
if len(t) < 2: continue
classes.append(t[0])
v = [float(x) for x in t[1: len(t) - 1]]
support.append(int(t[-1]))
class_names.append(t[0])
print(v)
plotMat.append(v)
Because I get the error:
ValueError: could not convert string to float: 'avg'
So the problem truly is how my classification report is being outputted. What can I change here to match the sample?
EDIT: what Ive tried:
df = pd.DataFrame(metrics.classification_report(valid_y, y_pred)).T
df['support'] = df.support.apply(int)
df.style.background_gradient(cmap='viridis',
subset=pd.IndexSlice['0':'9', :'f1-score'])
Error:
ValueError: DataFrame constructor not properly called!
With the advent of output_dict param in classification_report, there is no hassle for parsing the report. You can directly use the output of classification report to be read as pd.DataFrame. Then, you could use the pd.Style option to render the heat map.
Example:
from sklearn.metrics import classification_report
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, GridSearchCV
X, y = make_classification(n_samples=1000, n_features=30,
n_informative=12,
n_clusters_per_class=1, n_classes=10,
class_sep=2.0, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, stratify=y)
clf = LogisticRegression(max_iter=1000, random_state=42).fit(X_train, y_train)
df = pd.DataFrame(classification_report(clf.predict(X_test),
y_test, digits=2,
output_dict=True)).T
df['support'] = df.support.apply(int)
df.style.background_gradient(cmap='viridis',
subset=pd.IndexSlice['0':'9', :'f1-score'])
I'm taking a course and doing some examples my output comes wrong.
import pandas as pd
df = pd.read_csv(r'E:\Python Projects\Python-Data-Science-and-Machine-Learning-Bootcamp\Machine Learning\Árvores de decisão e Florestas Aleatórias\kyphosis.csv')
from sklearn.model_selection import train_test_split
x = df.drop('Kyphosis', axis=1)
y = df['Kyphosis']
X_train, X_test, y_train, y_test = train_test_split(x,y,test_size=0.33)
from sklearn.tree import DecisionTreeClassifier
dtree = DecisionTreeClassifier()
dtree.fit(X_train, y_train)
pred = dtree.predict(X_test)
from sklearn.metrics import classification_report
print(classification_report(y_test, pred))
These 2 data are missing
This is how classification_report returns the text summary, nothing is missing.
Look into the documentation: https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html
>>> from sklearn.metrics import classification_report
>>> y_true = [0, 1, 2, 2, 2]
>>> y_pred = [0, 0, 2, 2, 1]
>>> target_names = ['class 0', 'class 1', 'class 2']
>>> print(classification_report(y_true, y_pred, target_names=target_names))
precision recall f1-score support
<BLANKLINE>
class 0 0.50 1.00 0.67 1
class 1 0.00 0.00 0.00 1
class 2 1.00 0.67 0.80 3
<BLANKLINE>
accuracy 0.60 5
macro avg 0.50 0.56 0.49 5
weighted avg 0.70 0.60 0.61 5
<BLANKLINE>
>>> y_pred = [1, 1, 0]
>>> y_true = [1, 1, 1]
>>> print(classification_report(y_true, y_pred, labels=[1, 2, 3]))
precision recall f1-score support
<BLANKLINE>
1 1.00 0.67 0.80 3
2 0.00 0.00 0.00 0
3 0.00 0.00 0.00 0
<BLANKLINE>
micro avg 1.00 0.67 0.80 3
macro avg 0.33 0.22 0.27 3
weighted avg 1.00 0.67 0.80 3
<BLANKLINE>
The reported averages include macro average (averaging the unweighted mean per label), weighted average (averaging the support-weighted mean per label), and sample average (only for multilabel classification). Micro average (averaging the total true positives, false negatives and false positives) is only shown for multi-label or multi-class with a subset of classes, because it corresponds to accuracy otherwise.
Your accuracy is simply 74%.
Your classification report is not missing anything; it is a peculiarity of scikit-learn that it chooses to display the accuracy there, but there is no "precision accuracy" or "recall accuracy". Your actual accuracy is what is shown under the f1-score column; here is an example with toy data from the documentation:
from sklearn.metrics import classification_report
y_true = [0, 1, 2, 2, 2]
y_pred = [0, 0, 2, 2, 1]
target_names = ['class 0', 'class 1', 'class 2']
print(classification_report(y_true, y_pred, target_names=target_names))
Result:
precision recall f1-score support
class 0 0.50 1.00 0.67 1
class 1 0.00 0.00 0.00 1
class 2 1.00 0.67 0.80 3
accuracy 0.60 5
macro avg 0.50 0.56 0.49 5
weighted avg 0.70 0.60 0.61 5
i.e. the accuracy here is 0.6, something that you can directly verify:
from sklearn.metrics import accuracy_score
accuracy_score(y_true, y_pred)
# 0.6
You are right that it's odd, though, and it can certainly be confusing. Not a great design choice...
I split my dataset in two parts: training set and test set. For now just forget the test set and use the training set with the function GridSearchCV of the package sklearn.model_selection to search the best parameters for an SVM:
Cs = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
gammas = [0.001, 0.01, 0.1, 1]
# Set the parameters by cross-validation
param_grid = [{'kernel': ['rbf'], 'gamma': gammas, 'C': Cs}]
clf = GridSearchCV(svm.SVC(), param_grid = param_grid, cv=nfolds, verbose=1.)
clf.fit(x_train, labels)
after found my best C and gamma parameters, I create an SVM and I fit it with the training set (used before to search the best C and gamma):
model = svm.SVC(kernel='rbf', C = clf.best_params_['C'], gamma = clf.best_params_['gamma'])
model.fit(x_train, y_train)
At this point I tried one thing, I used the predict() function of the GridSearchCV object and the one of the svm.SVC object:
predicted_label1 = model.predict(x_test)
predicted_label2 = clf.predict(x_test)
and then I used the classification_report(y_test, predicted_label) to valuate my two predicted_label vectors. In my mind I should obtain the same values but this not happens...Here my output:
precision recall f1-score support
0.0 0.24 0.97 0.39 357
1.0 0.00 0.00 0.00 358
2.0 0.00 0.00 0.00 357
3.0 0.00 0.00 0.00 357
avg / total 0.06 0.24 0.10 1429
fine parametri
training set and test set saved
Create SVM classifier
precision recall f1-score support
0.0 0.70 0.63 0.66 357
1.0 0.89 0.90 0.90 358
2.0 0.89 0.94 0.91 357
3.0 0.85 0.88 0.86 357
avg / total 0.83 0.84 0.83 1429
The first is from the GridSearchCV and the second from the SVM...
Is this normal?
What does GridSearchCV returns? Does it fit with the passed training set?