Примеры кода на Kotlin
Версия от 23:19, 19 апреля 2020; Andreyantipov (обсуждение | вклад) (extend available libs list and descriptions)
Kotlin: https://kotlinlang.org/
Популярные библиотеки
- 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]
}
}
}
}