NumPy реализация алгоритма классификации Nearest Neighbor классифицирует все точно так же - PullRequest
0 голосов
/ 08 февраля 2019

Мое задание - использовать алгоритм K-Nearest Neighbor, чтобы определить, какой тип цветка основан на различных его свойствах (например, длина стебля, длина лепестка и т. Д.) С использованием NumPy.(Кстати, в прошлом я работал с Python, хотя это не мой «лучший» язык; тем не менее, я совершенно новичок в NumPy).

И мои данные об обучении, и мои данные тестирования находятся вCSV, которые выглядят так:

4.6,3.6,1.0,0.2,Iris-setosa
5.1,3.3,1.7,0.5,Iris-setosa
4.8,3.4,1.9,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor

Я знаю, как сделать основной алгоритм.Вот C #, который я создал для него:

namespace Project_3_Prototype
{
    public class FourD
    {
        public double f1, f2, f3, f4;

        public string name;

        public static double Distance(FourD a, FourD b)
        {
            double squared = Math.Pow(a.f1 - b.f1, 2) + Math.Pow(a.f2 - b.f2, 2) + Math.Pow(a.f3 - b.f3, 2) + Math.Pow(a.f4 - b.f4, 2);

            return Math.Sqrt(squared);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<FourD> distances = new List<FourD>();

            using (var parser = new TextFieldParser("iris-training-data.csv"))
            {
                parser.SetDelimiters(",");

                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();

                    var curr = new FourD
                    {
                        f1 = double.Parse(fields[0]),
                        f2 = double.Parse(fields[1]),
                        f3 = double.Parse(fields[2]),
                        f4 = double.Parse(fields[3]),
                        name = fields[4]
                    };

                    distances.Add(curr);
                }
            }

            double correct = 0, total = 0;

            using (var parser = new TextFieldParser("iris-testing-data.csv"))
            {
                parser.SetDelimiters(",");

                int i = 1;

                while (!parser.EndOfData)
                {
                    total++;
                    string[] fields = parser.ReadFields();

                    var curr = new FourD
                    {
                        f1 = double.Parse(fields[0]),
                        f2 = double.Parse(fields[1]),
                        f3 = double.Parse(fields[2]),
                        f4 = double.Parse(fields[3]),
                        name = fields[4]
                    };

                    FourD min = distances[0];

                    foreach (FourD comp in distances)
                    {
                        if (FourD.Distance(comp, curr) < FourD.Distance(min, curr))
                        {
                            min = comp;
                        }
                    }

                    if (min.name == curr.name)
                    {
                        correct++;
                    }

                    Console.WriteLine(string.Format("{0},{1},{2}", i, curr.name, min.name));

                    i++;
                }
            }

            Console.WriteLine("Accuracy: " + correct / total);

            Console.ReadLine();
        }
    }
}

Это работает точно так, как ожидается, со следующим выводом:

# The format is Number,Correct label,Predicted Label
1,Iris-setosa,Iris-setosa
2,Iris-setosa,Iris-setosa
3,Iris-setosa,Iris-setosa
4,Iris-setosa,Iris-setosa
5,Iris-setosa,Iris-setosa
6,Iris-setosa,Iris-setosa
7,Iris-setosa,Iris-setosa
8,Iris-setosa,Iris-setosa
9,Iris-setosa,Iris-setosa
10,Iris-setosa,Iris-setosa
11,Iris-setosa,Iris-setosa
12,Iris-setosa,Iris-setosa
...

Accuracy: 0.946666666666667

Я пытаюсь сделать то же самое в NumPy.Однако это назначение не позволяет мне использовать циклы for, только векторизованные функции.

Итак, в основном я хочу сделать следующее: для каждой строки в данных тестирования получить индекс строки всамые близкие к нему тренировочные данные (т. е. имеют минимальное евклидово расстояние).

Вот что я пробовал в Python:

import numpy as np

def main():    
    # Split each line of the CSV into a list of attributes and labels
    data = [x.split(',') for x in open("iris-training-data.csv")]

    # The last item is the label
    labels = np.array([x[-1].rstrip() for x in data])

    # Convert the first 3 items to a 2D array of floats
    floats = np.array([x[0:3] for x in data]).astype(float)

    classifyTrainingExamples(labels, floats)

def classifyTrainingExamples(labels, floats):
    # We're basically doing the same thing to the testing data that we did to the training data
    testingData = [x.split(',') for x in open("iris-testing-data.csv")]

    testingLabels = np.array([x[-1].rstrip() for x in testingData])

    testingFloats = np.array([x[0:3] for x in testingData]).astype(float)

    res = np.apply_along_axis(lambda x: closest(floats, x), 1, testingFloats)

    correct = 0

    for number, index in enumerate(res):    
        if labels[index] == testingLabels[number]:
            correct += 1

        print("{},{},{}".format(number + 1, testingLabels[number], labels[index]))

        number += 1

    print(correct / len(list(res)))

def closest(otherArray, item):
    res = np.apply_along_axis(lambda x: distance(x, item), 1, otherArray)

    i = np.argmin(res)

    return i

# Get the Euclidean distance between two "flat" lists (i.e. one particular row
def distance(a, b):
    # Subtract one from the other elementwise, then raise each one to the power of 2
    lst = (a - b) ** 2

    # Sum all of the elements together, and take the square root
    result = np.sqrt(lst.sum())

    return result

main()

К сожалению, вывод выглядит как

1,Iris-setosa,Iris-setosa
2,Iris-setosa,Iris-setosa
3,Iris-setosa,Iris-setosa
4,Iris-setosa,Iris-setosa
....
74,Iris-setosa,Iris-setosa
75,Iris-setosa,Iris-setosa
0.93333333

В каждой строке есть только 1023 * для меток, и точность равна 0,9333333.

Я попытался пройти через это с помощью отладчика, и каждый элемент считается какбыть корректным с помощью оператора if (но процент правильности по-прежнему отображается как 0,93333333).

Таким образом, в основном:

  • Это показывает, что каждый результат является "правильным" (когдаэто явно не так).
  • Это показывает Iris-setosa для каждого значения
  • Мой процент показывает как 93%.Правильное значение на самом деле составляет приблизительно 94%, но я ожидаю, что это покажет 100%, учитывая, что каждый результат предположительно «правильный».

Может ли кто-нибудь помочь мне увидеть, что мне здесь не хватает?

И прежде чем кто-нибудь спросит, для записи, да, я попробовал пройти через это с помощью отладчика :) Также для записи, да, это домашнее задание.

1 Ответ

0 голосов
/ 09 февраля 2019

Если вы действительно хотите сделать это в одну строку, вот что вы можете сделать (я скачал набор данных из scikit-learn):

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

# Load dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target
# Split training and test set
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=0.2)
# 1-neareast neighbour    
ypred = np.array([ytrain[np.argmin(np.sum((x-Xtrain)**2,axis=1))] for x in Xtest])
# Compute classification error
sum(ypred != ytest)/ len(ytest)

Теперь это 1-ближайший сосед, этотолько смотрит на ближайшую точку из тренировочного набора.Для k-ближайшего соседа вы должны изменить его следующим образом:

# k-neareast neighbour    
k = 3
ypredk = np.array([np.argmax(np.bincount(ytrain[np.argsort(np.sum((x-Xtrain)**2,axis=1))[0:k]])) for x in Xtest])
sum(ypredk != ytest)/ len(ytest)

Чтобы выразить это словами, вы сортируете расстояния, вы найдете индексы k самых низких значений (это часть np.argsort)) и соответствующие метки, то вы ищете наиболее распространенную метку среди k (это часть np.argmax(np.bincount(x))).

Наконец, если вы хотите убедиться, вы можете сравнить с scikit-learn:

# scikit-learn NN
from sklearn import neighbors
knn = neighbors.KNeighborsClassifier(n_neighbors=k, algorithm='ball_tree')
knn.fit(Xtrain,ytrain)
ypred_sklearn = knn.predict(Xtest)
sum(ypred_sklearn != ytest)/ len(ytest)
...