<commit_before>//Some code from https://machinelearningmastery.com/implement-perceptron-algorithm-scratch-python/
//Some from Neural networks course by <NAME>

package SingleLayerPerceptron

import scala.io.Source

object Main extends App {

  def readData(): Vector[Vector[Double]] = {
    val src: Iterator[String] = Source.fromFile("src/data.txt").getLines

    val splitLines: Iterator[Array[String]] = src.map(_.split(","))

    def lineToDoubles(line: Array[String]): Vector[Double] = {
      val className: Double = if(line.last == "R") 1 else 0
      val features = line.dropRight(1).map(_.toDouble)
      features.toVector :+ className
    }

    splitLines.map(lineToDoubles).toVector
  }

  println(readData())
}

object SingleLayerPerceptron {
  def predict(row: Vector[Double], weights: Vector[Double]): Int = {
    /* bias + w0 + w1 * x1 + w2 * x2 */
    val activation = weights.head + row
      .drop(1)
      .zipWithIndex
      .foldLeft(0.0)((prev, curr: (_, Int)) =>
        prev + weights(curr._2 + 1) * row(curr._2))

    if (activation >= 0.0) 1 else 0
  }

  def trainNetwork(
    rows: Vector[Vector[Double]],
    learningRate: Double,
    iterations: Int): Vector[Double] = {

    def trainWeights(weights: Vector[Double], nth: Int): Vector[Double] = {
      if (nth == 0) weights else {

        var sumError = 0.0

        def trainFromSet(
          weights: Vector[Double],
          rows: Vector[Vector[Double]]): Vector[Double] = {
          if (rows.isEmpty) weights else {
            val row = rows.head
            val prediction = predict(row, weights)
            val error = row.last - prediction
            sumError += Math.pow(error, 2)

            val biasWeight: Double = weights(0) + learningRate * error
            val updatedWeights = biasWeight +: row.drop(1).zipWithIndex.map { case (_, i) =>
              weights(i + 1) + learningRate * error * row(i)
            }
            println(s"iteration: $nth, error: $sumError")

            trainFromSet(updatedWeights, rows.tail)
          }
        }

        val updatedWeights = trainFromSet(weights, rows)

        trainWeights(updatedWeights, nth - 1)
      }
    }

    trainWeights(Vector.fill(rows.head.length)(0), iterations)
  }
}
<commit_msg>feat(project) dataset accuracy and training<commit_after>//Some code from https://machinelearningmastery.com/implement-perceptron-algorithm-scratch-python/
//Some from Neural networks course by <NAME>

package SingleLayerPerceptron

import scala.io.Source

object Main extends App {

  def readData(): Vector[Vector[Double]] = {
    val src: Iterator[String] = Source.fromFile("src/data.txt").getLines

    val splitLines: Iterator[Array[String]] = src.map(_.split(","))

    def lineToDoubles(line: Array[String]): Vector[Double] = {
      val className: Double = if(line.last == "R") 1 else 0
      val features = line.dropRight(1).map(_.toDouble)
      features.toVector :+ className
    }

    splitLines.map(lineToDoubles).toVector
  }

  val allData = readData()

  val trainingData = allData.dropRight((allData.length * 0.2).toInt)
  val testData = allData.drop((allData.length * 0.8).toInt)

  val weights = SingleLayerPerceptron.trainNetwork(trainingData, 0.1, 500)
  val predictions = testData.map(r => SingleLayerPerceptron.predict(r, weights) -> r.last)

  val howManyPredictedCorrect: Double = predictions.map(c => c._1 == c._2.toInt).count(_ == true)

  val meanAccuracy: Double = howManyPredictedCorrect / predictions.length

  println(weights)
  println(meanAccuracy * 100)

}

object SingleLayerPerceptron {
  def predict(row: Vector[Double], weights: Vector[Double]): Int = {
    /* bias + w0 + w1 * x1 + w2 * x2 */
    val activation = weights.head + row
      .drop(1)
      .zipWithIndex
      .foldLeft(0.0)((prev, curr: (_, Int)) =>
        prev + weights(curr._2 + 1) * row(curr._2))

    if (activation >= 0.0) 1 else 0
  }

  def trainNetwork(
    rows: Vector[Vector[Double]],
    learningRate: Double,
    iterations: Int): Vector[Double] = {

    def trainWeights(weights: Vector[Double], nth: Int): Vector[Double] = {
      if (nth == 0) weights else {

        var sumError = 0.0

        def trainFromSet(
          weights: Vector[Double],
          rows: Vector[Vector[Double]]): Vector[Double] = {
          if (rows.isEmpty) weights else {
            val row = rows.head
            val prediction = predict(row, weights)
            val error = row.last - prediction
            sumError += Math.pow(error, 2)

            val biasWeight: Double = weights(0) + learningRate * error
            val updatedWeights = biasWeight +: row.drop(1).zipWithIndex.map { case (_, i) =>
              weights(i + 1) + learningRate * error * row(i)
            }
//            println(s"iteration: $nth, error: $sumError")

            trainFromSet(updatedWeights, rows.tail)
          }
        }

        val updatedWeights = trainFromSet(weights, rows)

        trainWeights(updatedWeights, nth - 1)
      }
    }

    trainWeights(Vector.fill(rows.head.length)(0), iterations)
  }
}

{'fraction_non_alphanumeric': 0.12971372161895361, 'fraction_numerical': 0.013425468904244817, 'mean_word_length': 2.7195301027900145, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 14, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1699157', 'n_tokens_mistral': 1643, 'n_tokens_neox': 1557, 'n_words': 364}