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?
Related
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'm performing SVM on a dataset with 10 folds for different values of C and gamma
from sklearn.datasets import load_digits, load_iris, load_breast_cancer, load_wine
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.utils import shuffle
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score, zero_one_loss, confusion_matrix
import pandas as pd
import numpy as np
z = pd.read_csv('/home/user/iris.csv', header=0)
X = z.iloc[:, :-1]
y = z.iloc[:, -1:]
X = np.array(X)
y = np.array(y)
# Performing standard scaling
scaler = preprocessing.MinMaxScaler()
X_scaled = scaler.fit_transform(X)
c = [0.1, 0.5]
gamma_values = [1e-1, 1]
for z in c:
for v in gamma_values:
# Defining the SVM with 'rbf' kernel
svc = SVC(kernel='rbf',C=z, gamma=v, random_state=50)
skf = StratifiedKFold(n_splits=10, shuffle=True)
acc_score = []
#skf.get_n_splits(X, y)
for train_index, test_index in skf.split(X, y):
X_train, X_test = X_scaled[train_index], X_scaled[test_index]
y_train, y_test = y[train_index], y[test_index]
# Training the model
svc.fit(X_train, np.ravel(y_train))
# Prediction on test dataste
y_pred = svc.predict(X_test)
# Obtaining the accuracy scores of the model
score = accuracy_score(y_test, y_pred)
acc_score.append(score)
print(np.array(acc_score))
#print the accuracy score for each of the C values
print('Mean accuracy score: %0.3f' % np.array(acc_score).mean())
which results in output like below
[0.52 0.6 0.49 0.6 0.55 0.6 0.5 0.51 0.63 0.54]
Mean accuracy score: 0.554
[0.51 0.45 0.54 0.42 0.53 0.45 0.52 0.48 0.5 0.39]
Mean accuracy score: 0.479
[0.73 0.76 0.7 0.64 0.61 0.68 0.71 0.61 0.71 0.71]
Mean accuracy score: 0.686
[0.76 0.6 0.66 0.61 0.67 0.66 0.69 0.74 0.63 0.65]
Mean accuracy score: 0.667
However, I want to print the results more meaningfully like below :
[0.52 0.6 0.49 0.6 0.55 0.6 0.5 0.51 0.63 0.54]
Mean accuracy score for (C=0.1,gamma=0.1): 0.554
[0.51 0.45 0.54 0.42 0.53 0.45 0.52 0.48 0.5 0.39]
Mean accuracy score (C=0.1, gamma = 1): 0.479
[0.73 0.76 0.7 0.64 0.61 0.68 0.71 0.61 0.71 0.71]
Mean accuracy score (C=0.5, gamma = 0.1): 0.686
[0.76 0.6 0.66 0.61 0.67 0.66 0.69 0.74 0.63 0.65]
Mean accuracy score (C=0.5, gamma = 1): 0.667
How can I print the results more meaningfully in the existing code ?
try this:
# (1)
print('Mean accuracy score (C=%0.1f, gamma=%0.1f): %0.3f' % (z, v, np.array(acc_score).mean()))
# (2)
print("Mean accuracy score (C={}, gamma={}): {}".format(z, v, np.array(acc_score).mean()))
# (3)
print("Mean accuracy score (C="+str(z)+", gamma="+str(v)+"): "+str(np.array(acc_score).mean()))
output:
Mean accuracy score (C=0.1, gamma=0.1): 0.554
You can use fancy output formatting:
print(f'Mean accuracy score (C={z:.1f}, gamma={v:.1f}): {np.array(acc_score).mean()}')
I want to calculate the F1 score of my models. But I receive a warning and get a 0.0 F1-score and I don't know what to do.
here is the source code:
def model_evaluation(dict):
for key,value in dict.items():
classifier = Pipeline([('tfidf', TfidfVectorizer()),
('clf', value),
])
classifier.fit(X_train, y_train)
predictions = classifier.predict(X_test)
print("Accuracy Score of" , key , ": ", metrics.accuracy_score(y_test,predictions))
print(metrics.classification_report(y_test,predictions))
print(metrics.f1_score(y_test, predictions, average="weighted", labels=np.unique(predictions), zero_division=0))
print("---------------","\n")
dlist = { "KNeighborsClassifier": KNeighborsClassifier(3),"LinearSVC":
LinearSVC(), "MultinomialNB": MultinomialNB(), "RandomForest": RandomForestClassifier(max_depth=5, n_estimators=100)}
model_evaluation(dlist)
And here is the result:
Accuracy Score of KNeighborsClassifier : 0.75
precision recall f1-score support
not positive 0.71 0.77 0.74 13
positive 0.79 0.73 0.76 15
accuracy 0.75 28
macro avg 0.75 0.75 0.75 28
weighted avg 0.75 0.75 0.75 28
0.7503192848020434
---------------
Accuracy Score of LinearSVC : 0.8928571428571429
precision recall f1-score support
not positive 1.00 0.77 0.87 13
positive 0.83 1.00 0.91 15
accuracy 0.89 28
macro avg 0.92 0.88 0.89 28
weighted avg 0.91 0.89 0.89 28
0.8907396950875212
---------------
Accuracy Score of MultinomialNB : 0.5357142857142857
precision recall f1-score support
not positive 0.00 0.00 0.00 13
positive 0.54 1.00 0.70 15
accuracy 0.54 28
macro avg 0.27 0.50 0.35 28
weighted avg 0.29 0.54 0.37 28
0.6976744186046512
---------------
C:\Users\Cey\anaconda3\lib\site-packages\sklearn\metrics\_classification.py:1272: UndefinedMetricWarning: 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.
_warn_prf(average, modifier, msg_start, len(result))
Accuracy Score of RandomForest : 0.5714285714285714
precision recall f1-score support
not positive 1.00 0.08 0.14 13
positive 0.56 1.00 0.71 15
accuracy 0.57 28
macro avg 0.78 0.54 0.43 28
weighted avg 0.76 0.57 0.45 28
0.44897959183673475
---------------
Can someone tell me what to do? I only receive this message when using the "MultinomialNB()" classifier
Second:
When extending the dictionary by using the Gausian classifier (GaussianNB()) I receive this error message:
TypeError: A sparse matrix was passed, but dense data is required. Use X.toarray() to convert to a dense numpy array.
What should I do here ?
Together with UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples (main credits go there) and #yatu's answer, I could at least find a workaround for the warning:
UndefinedMetricWarning: Precision is ill-defined and being set to 0.0
due to no predicted samples. Use zero_division parameter to control
this behavior. _warn_prf(average, modifier, msg_start, len(result))
Quote from sklearn.metrics.f1_score in the Notes at the bottom:
When true positive + false positive == 0, precision is undefined. When
true positive + false negative == 0, recall is undefined. In such
cases, by default the metric will be set to 0, as will f-score, and
UndefinedMetricWarning will be raised. This behavior can be modified
with zero_division.
Thus, you cannot avoid this error if your data does not output a difference between true positives and false positives.
That being said, you can only suppress the warning at least, adding zero_division=0 to the functions mentioned in the quote. In either case, set to 0 or 1, you will get a 0 value as the return anyway.
precision = precision_score(y_test, y_pred, zero_division=0)
print('Precision score: {0:0.2f}'.format(precision))
recall = recall_score(y_test, y_pred, zero_division=0)
print('Recall score: {0:0.2f}'.format(recall))
f1 = f1_score(y_test, y_pred, zero_division=0)
print('f1 score: {0:0.2f}'.format(recall))
Can someone tell me what to do? I only receive this message when using the "MultinomialNB()" classifier
The first error seems to be indicating that a specific label is not predicted when using the MultinomialNB, which results in an undefined f-score, or ill-defined, since the missing values are set to 0. This is explained here
When extending the dictionary by using the Gausian classifier (GaussianNB()) I receive this error message:
TypeError: A sparse matrix was passed, but dense data is required. Use X.toarray() to convert to a dense numpy array.
As per this question, the error is quite explicit, the issue is that TfidfVectorizer is returning a sparse matrix, which cannot be used as input for the GaussianNB. So the way I see it, you either avoid using the GaussianNB, or you add an intermediate transformer to turn the sparse array to dense, which I wouldn't advise being the result of a tf-idf vectorization.
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...