[Introduction à l'informatique, partie 2: Essayez l'apprentissage automatique] Implémentons la méthode de calcul de la moyenne k dans Java-Distance entre les données-

introduction

Bonjour, c'est de l'eau Sumiyama.

Suite de la session précédente. [Introduction à l'informatique Partie 1: Essayons l'apprentissage automatique] Implémentons la méthode de calcul de la moyenne k en Java-À propos du concept de coordonnées-

La dernière fois, j'ai essayé le concept de coordonnées pour organiser des données sur un plan ou un espace et les exprimer en Java.

Cette fois, je parlerai du concept de distance entre les données.

environnement

J'ai écrit sur la préparation de cet environnement dans cet article. Notes lors du démarrage d'un nouveau développement avec IntelliJ + Gradle + SpringBoot + JUnit5 (jupiter)

Quand vous y repensez, quelle est la distance?

Comme je l'ai mentionné dans la partie 0, ce que je veux faire avec le clustering est de ** collecter des données proches **, mais je définirai ensuite que ** les données sont proches les unes des autres **. est nécessaire.

Par exemple, dans le cas illustré sur la figure

image.png

Eh bien, c'est évident pour l'œil humain, mais comme c'est l'ordinateur qui le calcule, ** près ** / ** loin ** est exprimé numériquement.

Par conséquent, dans l'ordinateur, en utilisant la valeur ** distance **,

――Plus la valeur de la distance est élevée, plus

Vous pourrez comparer numériquement comme ceci [^ 1].

Utiliser la distance euclidienne

En fait, il y a tellement de façons de calculer la distance que la recherche peut être complétée par elle-même, mais cette fois j'utiliserai la ** distance euclidienne **, qui est facile à calculer et le mécanisme est visuellement facile à comprendre.

Il a un grand nom, mais en bref, c'est une distance normale ** que l'on peut voir lors de la mesure entre deux points avec une règle. Cependant, il n'est pas possible de laisser l'ordinateur utiliser une règle pour chaque donnée, elle est donc calculée par calcul.

image.png

Veuillez consulter la figure. Vous souvenez-vous du ** Théorème des Trois Carrés **? Le fait est que. Les points placés aux coordonnées [1,4] et les points placés aux coordonnées [5,1] sont la direction horizontale (le premier élément de chaque coordonnée) et la direction verticale (le deuxième élément de chaque coordonnée). ) Est le carré de la différence, la somme est la racine carrée et la distance est de 5.

À propos, il peut être étendu autant que vous le souhaitez car il peut être calculé de la même manière, qu'il soit 3D ou supérieur.

Les distances dans les trois dimensions [3,1,2] et [1,0,6] sont image.png Ce sera. Au fur et à mesure que le nombre de dimensions augmente, vous pouvez augmenter le nombre à ajouter.

Implémentons

package net.tan3sugarless.clusteringsample.lib.data;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.Value;
import net.tan3sugarless.clusteringsample.exception.DimensionNotUnifiedException;
import net.tan3sugarless.clusteringsample.exception.NullCoordinateException;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 *Coordonnées définies sur l'espace de distance euclidien
 */
@Getter
@ToString
@EqualsAndHashCode
@Value
public class EuclideanSpace {

    private final List<List<Double>> points;
    private final int dimension;

    /**
     *Définir une liste de coordonnées et de dimensions à n dimensions
     *
     * @param points :Liste des coordonnées à n dimensions
     * @throws DimensionNotUnifiedException :J'ai défini une liste où les dimensions des coordonnées ne sont pas unifiées
     * @throws NullCoordinateException      :La valeur numérique de la coordonnée contenait null
     * @throws NullPointerException         :Données nulles transmises ou données contenant des éléments nuls
     */
    public EuclideanSpace(List<List<Double>> points) {
        if (points.stream().mapToInt(List::size).distinct().count() > 1) {
            throw new DimensionNotUnifiedException();
        }
        if (points.stream().anyMatch(point -> point.stream().anyMatch(Objects::isNull))) {
            throw new NullCoordinateException();
        }

        this.points = points;
        this.dimension = points.stream().findFirst().map(List::size).orElse(0);
    }

    /**
     *Calculez la distance de chaque coordonnée stockée dans cette instance à partir de n'importe quelle coordonnée
     *
     * @param target Coordonnées du point de référence où vous voulez calculer la distance de chaque coordonnée
     * @Une liste des distances euclidiennes de la cible de retour
     * @throws DimensionNotUnifiedException :Les dimensions de la cible et de l'instance sont différentes
     * @throws NullCoordinateException      :la cible contient null
     */
    public List<Double> distanceFromTarget(List<Double> target) {
        if (target.size() != dimension) {
            throw new DimensionNotUnifiedException();
        }

        if (target.stream().anyMatch(Objects::isNull)) {
            throw new NullCoordinateException();
        }

        return points.stream().map(point -> {
            double squareOfDistance = 0.0;
            for (int i = 0; i < target.size(); i++) {
                squareOfDistance += Math.pow(point.get(i) - target.get(i), 2);
            }

            return Math.sqrt(squareOfDistance);
        }).collect(Collectors.toList());
    }
}

Nous le créerons dans la même classe que Last time.

Tout d'abord, nous avons modifié un peu les constructeurs et les champs pour faciliter la vérification des dimensions.

    private final int dimension;

J'ai rendu possible le stockage des dimensions dans le champ int,

        this.dimension = points.stream().findFirst().map(List::size).orElse(0);

C'est un ajout de logique pour calculer la dimension des données acquises.

Et voici la méthode pour calculer réellement la distance. Dans une implémentation future, je veux calculer "la distance entre un certain point et toutes les coordonnées", donc cela ressemble à ceci.

    /**
     *Calculez la distance de chaque coordonnée stockée dans cette instance à partir de n'importe quelle coordonnée
     *
     * @param target Coordonnées du point de référence où vous voulez calculer la distance de chaque coordonnée
     * @Une liste des distances euclidiennes de la cible de retour
     * @throws DimensionNotUnifiedException :Les dimensions de la cible et de l'instance sont différentes
     * @throws NullCoordinateException      :la cible contient null
     */
    public List<Double> distanceFromTarget(List<Double> target) {
        if (target.size() != dimension) {
            throw new DimensionNotUnifiedException();
        }

        if (target.stream().anyMatch(Objects::isNull)) {
            throw new NullCoordinateException();
        }

        return points.stream().map(point -> {
            double squareOfDistance = 0.0;
            for (int i = 0; i < target.size(); i++) {
                squareOfDistance += Math.pow(point.get(i) - target.get(i), 2);
            }

            return Math.sqrt(squareOfDistance);
        }).collect(Collectors.toList());
    }

Soudain, Math :: pow ou Math :: sqrt apparaît, mais pow est une puissance et sqrt est une méthode qui prend une racine carrée. Les mathématiques sont une classe avec laquelle vous n'êtes pas familier dans les services Web et les applications métier, mais n'oubliez pas qu'elle est souvent utilisée dans les calculs arithmétiques.

Je veux faire quelque chose comme ça une fois illustré.

image.png

Et tester

package net.tan3sugarless.clusteringsample.lib.data;

import net.tan3sugarless.clusteringsample.exception.DimensionNotUnifiedException;
import net.tan3sugarless.clusteringsample.exception.NullCoordinateException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

class EuclideanSpaceTest {

    //Nul entier,Ciel,1 élément,Éléments multiples
    //Chaque élément contient null,Y compris le ciel,Tout est vide(0 dimension),1 dimension,n dimensions
    //Inclut les coordonnées nulles dans chaque élément,Y compris 0,Ne contient pas de valeur nulle
    //Contrôle des dimensions Tous de la même dimension,Différentes dimensions
    static Stream<Arguments> testConstructorProvider() {
        //@formatter:off
        return Stream.of(
                Arguments.of(null, new NullPointerException(), 0),
                Arguments.of(Collections.emptyList(), null, 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, -2.1)), null, 2),
                Arguments.of(Arrays.asList(Arrays.asList(1.2, 0.1), Arrays.asList(0.0, 1.5)), null, 2),
                Arguments.of(Arrays.asList(null, Arrays.asList(0, 1.5), Arrays.asList(-0.9, 0.1)), new NullPointerException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(-0.9, 0.1), Arrays.asList(0.0, 1.5), Collections.emptyList()), new DimensionNotUnifiedException(), 0),
                Arguments.of(Arrays.asList(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()), null, 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5), Arrays.asList(0.0), Arrays.asList(-2.2)), null, 1),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, 2.2, -1.9), Arrays.asList(0.0, 0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), null, 3),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, null, -1.9), Arrays.asList(0.0, 0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), new NullCoordinateException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, 2.1, -1.9), Arrays.asList(0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), new DimensionNotUnifiedException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(2.1, -1.9), Arrays.asList(0, 0, 0), Arrays.asList(0.9, 5.0, 2.2)), new DimensionNotUnifiedException(), 0)
        );
        //@formatter:on
    }

    @ParameterizedTest
    @MethodSource("testConstructorProvider")
    @DisplayName("Tester le constructeur")
    void testConstructor(List<List<Double>> points, RuntimeException e, int dimension) {
        if (e == null) {
            Assertions.assertDoesNotThrow(() -> new EuclideanSpace(points));

            EuclideanSpace actual = new EuclideanSpace(points);
            Assertions.assertEquals(dimension, actual.getDimension());
        } else {
            Assertions.assertThrows(e.getClass(), () -> new EuclideanSpace(points));
        }
    }

    // points :0 cas/1/2 cas,0 dimension/1 dimension/2D, 0/Positif/négatif
    // target : null/Ciel/1 dimension/2D,Y compris nul/Non inclus, 0/Positif/négatif/Mêmes coordonnées
    static Stream<Arguments> testDistanceFromTargetProvider() {
        return Stream.of(
                //@formatter:off
                Arguments.of(Collections.emptyList(), Collections.emptyList(), null, Collections.emptyList()),
                Arguments.of(Collections.emptyList(), Arrays.asList(0.1), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Collections.emptyList()), Collections.emptyList(), null, Arrays.asList(0.0)),
                Arguments.of(Arrays.asList(Collections.emptyList()), Arrays.asList(0.1), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(3.0)), Arrays.asList(1.0), null, Arrays.asList(2.0)),
                Arguments.of(Arrays.asList(Arrays.asList(3.0)), Arrays.asList(1.0, 2.0), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(-1.0, 0.0)), Arrays.asList(2.0, -4.0), null, Arrays.asList(5.0)),
                Arguments.of(Arrays.asList(Arrays.asList(-1.0, 0.0)), Arrays.asList(null, -4.0), new NullCoordinateException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(-3.0, 0.0), Arrays.asList(0.0, -4.0)), Arrays.asList(0.0, -4.0), null, Arrays.asList(5.0, 0.0))
                //@formatter:on
        );
    }

    @ParameterizedTest
    @MethodSource("testDistanceFromTargetProvider")
    @DisplayName("Test de calcul de distance")
    void testDistanceFromTarget(List<List<Double>> points, List<Double> target, RuntimeException e, List<Double> distances) {
        EuclideanSpace space = new EuclideanSpace(points);

        if (e == null) {
            Assertions.assertEquals(distances, space.distanceFromTarget(target));
        } else {
            Assertions.assertThrows(e.getClass(), () -> space.distanceFromTarget(target));
        }
    }
}

La version implémentée cette fois est publiée dans cette version de GitHub. https://github.com/tan3nonsugar/clusteringsample/releases/tag/v0.0.2

Merci d'avoir lu jusqu'ici. La prochaine fois, je parlerai de l'idée de "centre de données". Puis ~ Noshi

[^ 1]: Strictement parlant, nous devrions d'abord expliquer la dissimilarité / similitude, ou parler du mécontentement qui répond à la définition mathématique de la distance, mais donner la priorité à l'obtention d'un sens. Je vais l'omettre ici.

Recommended Posts

[Introduction à l'informatique, partie 2: Essayez l'apprentissage automatique] Implémentons la méthode de calcul de la moyenne k dans Java-Distance entre les données-
[Introduction à l'informatique Partie 3: Essayons l'apprentissage automatique] Implémentons la méthode de moyennage k dans Java-Center of data set-
[Introduction à l'informatique n ° 0: Essayez l'apprentissage automatique] Implémentons la méthode de calcul de la moyenne k en Java
[Introduction à l'informatique Partie 1: Essayons l'apprentissage automatique] Implémentons la méthode de calcul de la moyenne k en Java - À propos du concept de coordonnées -
Essayez d'implémenter Yuma dans Ruby
Essayez d'implémenter Yuma en Java
Essayez d'implémenter l'ajout n-aire en Java