Примеры кода на Kotlin
Версия от 23:19, 19 апреля 2020; Andreyantipov (обсуждение | вклад) (extend available libs list and descriptions)
Содержание
Популярные библиотеки
- Kotlin-statistics[1] — библиотека с набором функций-расширений для работы с коллекциями, такими как mode, median, range, variance, standardDeviation, geometricMean и др. Также, библиотека предоставляет расширения для трансформации коллекций и агрегации данных. Есть реализации Наивного Баесового Классификаторы, алгоритмов классификации, линейной регрессии.
- KMath[2] — аналог numpy: поддержка алгебраических структур, массиво-подобных коллекций, гистограмм и т.д.
- SMILE[3] — JVM фреймворк, для которого помимо официального API[4] на Kotlin существуют расширения SMILE-NLP-KT[5]. Фреймворк используется для решения различных задач машинного обучения, таких как: классификация, регрессия, кластеризация, использование генетических алгоритмов, KNN, вывод отсутствующих значений набора данных, обработку естественных языков. Есть встроенная поддержка визуализации данных, средств для чтения и нормализации данных различных форматов (csv, apache arrow, json, jdbc).
Так как Kotlin интеропабилен с Java, то помимо поддерживающих для Kotlin библиотек можно использовать библиотеки для Java, например:
- Deeplearning4j[6] — DSL на Java для конфигурации глубоких нейронных сетей. Библиотека поддерживает распределенные вычисления, используя Apache Spark. Помимо реализаций алгоритмов машинного обучения, библиотека содержит классы для загрузки и нормализации данных.
Также есть возможность[7] работы с NumPy.
Примеры кода
Примеры кода написаны на kotlin 1.3.71 для JVM, с использованием kotlin-statistics
Gradle зависимость:
repositories { maven { url 'https://jitpack.io' } } dependencies { implementation 'com.github.thomasnield:kotlin-statistics:-SNAPSHOT' }
Линейная регрессия
Пример линейной регрессии c применением Kotlin-statistics:
fun main() { val r = sequenceOf( 1.0 to 3.0, 2.0 to 6.0, 3.0 to 9.0, 4.0 to 11.8 ).simpleRegression() println(r.slope) // 2.9400000000000004 println(r.meanSquareError) // 0.006000000000000227 println(r.predict(5.0)). // 14.8 }
Байесовская классификация
Основная статья: Байесовская классификация.
Пример классификации при помощи Наивного Байесовского Классификатора:
import org.nield.kotlinstatistics.toNaiveBayesClassifier class Email(val message: String, val isSpam: Boolean) fun main() { val emails = listOf( Email("Hey! If you really want to enlarge your ML scores click here", isSpam = true), Email("Earn 50 more points for ML just by visiting this site!", isSpam = true), Email("Still have F grade? Professional help with ML right here", isSpam = true), Email("Hey, I left my phone at home. Email me if you need anything.", isSpam = false), Email("Stay At Home: COVID-19 news", isSpam = false), Email("Please see attachment for notes on today's meeting.", isSpam = false), Email("JetBrains license certificate", isSpam = false), Email("Your Education Pack expires soon ", isSpam = false) ) val nbc = emails.toNaiveBayesClassifier( featuresSelector = { it.message.splitWords().toSet() }, categorySelector = { it.isSpam } ) val spamInput = "your grade is still so bad, but I can help you to get more scores".splitWords().toSet() require(nbc.predict(spamInput) == true) { spamInput } val legitInput = "Thank you for placing the order ".splitWords().toSet() require(nbc.predict(legitInput) == false) { legitInput } } fun String.splitWords(): Sequence<String> = this.split(Regex("\\s")) .asSequence() .map { it.replace(Regex("[^A-Za-z]"), "") } .map { it.toLowerCase() } .filter { it.isNotEmpty() }
Кластеризация
Основная статья: Кластеризация.
Пример кластеризации с DBSCAN:
import org.nield.kotlinstatistics.dbScanCluster import kotlin.math.pow import kotlin.math.sin inline fun <V> IntProgression.mapDouble(mapper: (Double) -> V) = this.map { mapper(it.toDouble()) } data class Point(val coordinates: Pair<Double, Double>, val cluster: Int) fun main() { val firstCluster = (1..100 step 1) .mapDouble { x -> Point(x to x / 2, cluster = 1) } val secondCluster = (1..80 step 3) .mapDouble { x -> Point(x to (x / 12).pow(2) + 20, cluster = 2) } val thirdCluster = (60..150 step 1) .mapDouble { x -> Point(x to 10 * sin(x / 5) + 15, cluster = 3) } val points = firstCluster + secondCluster + thirdCluster val clusters = points.dbScanCluster( xSelector = { (coords) -> coords.first }, ySelector = { (coords) -> coords.second }, maximumRadius = 5.0, minPoints = 1 ) val pointsWithMatchedClusters = clusters.withIndex() .flatMap { (clusterIdx, matched) -> matched.points.map { p -> p to clusterIdx + 1 } } require(clusters.size == 3) { clusters.size } val pointsWithMismatchedCluster = pointsWithMatchedClusters.filterNot { (p, cluster) -> cluster == p.cluster } require(pointsWithMismatchedCluster.isEmpty()) { pointsWithMatchedClusters } }
Пример работы с матрицами
Пример использования средств языка и методов стандартной библиотеки для работы с матрицами
typealias Vector = List<Double> typealias Matrix = List<Vector> class MatrixBuilder { private var matrixWidth: Int? = null private val _result: MutableList<Vector> = mutableListOf() val result: Matrix = _result operator fun invoke(vararg vector: Double) = addVector(vector.toList()) operator fun invoke(vararg vector: Number) = addVector(vector.map { it.toDouble() }) private fun addVector(vectorList: List<Double>) { _result.add(vectorList) if (matrixWidth != null) { require(vectorList.size == matrixWidth) { "Vector size must be the same among all builder invocations: $vectorList, $_result" } } else { matrixWidth = vectorList.size } } } fun matrix(builder: MatrixBuilder.() -> Unit): Matrix = MatrixBuilder().apply(builder).result fun main() { val multiplied = matrix { this(1, 2, 3, 4) this(1, 2, 3, 4) this(1, 2, 3, 4) } * matrix { this(5, 6) this(7, 8) this(9, 10) this(11, 12) } multiplied .transpose() .print() } fun Matrix.transpose(): Matrix = this.asSequence() .map { it.withIndex() } .flatten() .groupBy({ it.index }, { it.value }) .values .toList() operator fun Matrix.times(other: Matrix): Matrix { val (rows1, cols1) = this.size() val (_, cols2) = other.size() return (0 until rows1).map { i -> (0 until cols2).map { j -> (0 until cols1).fold(0.0) { s, k -> s + this[i][k] * other[k][j] } } } } fun Matrix.size(): Pair<Int, Int> = this.size to this.first().size fun Pair<Int, Int>.zeroMatrix(): Matrix = List(this.first) { List(this.second) { 0.0 } } fun Matrix.print() = println(this.joinToString(separator = "\n") { it.joinToString(separator = " ") }) fun List<Matrix>.sum(): Matrix { val n = this.size val (rowsCount, colCount) = this[0].size() return (0 until rowsCount).map { i -> (0 until colCount).map { j -> (0 until n).fold(0.0) { s, k -> s + this[k][i][j] } } } }