├── .gitignore
├── .gitmodules
├── .scalafmt.conf
├── .travis.yml
├── README.md
├── appveyor.yml
├── build.sbt
├── cover.jpeg
├── local.sbt
├── project
├── build.properties
└── plugins.sbt
└── src
└── main
├── resources
├── get_datasets.sh
└── logback.xml
└── scala
└── com
└── github
└── izhangzhihao
├── GettingStarted.scala
├── MiniBatchGradientDescent.scala
├── ReadCIFAR10.scala
├── ReadCIFAR10ToNDArray.scala
├── SoftmaxLinearClassifier.scala
├── TwoLayerNet.scala
└── Utils.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | project/target
2 | target
3 | notebook/.ipynb_checkpoints
4 | *.html
5 | src/main/resources/cifar-10-batches-bin
6 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "notebooks"]
2 | path = notebooks
3 | url = git://github.com/thoughtworksinc/deeplearning.scala-website.git
4 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | spaces.neverAroundInfixTypes = ["##"]
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | include:
3 | - os: linux
4 | jdk: oraclejdk8
5 | sudo: false
6 | language: scala
7 | addons:
8 | apt:
9 | packages:
10 | - graphviz
11 | - os: osx
12 | osx_image: xcode8
13 | language: java
14 | before_install:
15 | - brew update
16 | - brew install sbt
17 |
18 | before_cache:
19 | - find $HOME/.sbt -name '*.lock' -delete
20 | - find $HOME/.ivy2 -name 'ivydata-*.properties' -delete
21 |
22 | cache:
23 | directories:
24 | - $HOME/.ivy2/cache
25 | - $HOME/.sbt/boot/
26 |
27 | script:
28 | - sbt compile
29 | - sbt "runMain com.github.izhangzhihao.GettingStarted"
30 | - sbt "runMain com.github.izhangzhihao.SoftmaxLinearClassifier"
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial of DeepLearning.scala [](https://travis-ci.org/izhangzhihao/deeplearning-tutorial) [](https://ci.appveyor.com/project/izhangzhihao/deeplearning-tutorial/branch/master) [](https://www.codacy.com/app/izhangzhihao/deeplearning-tutorial?utm_source=github.com&utm_medium=referral&utm_content=izhangzhihao/deeplearning-tutorial&utm_campaign=badger)
2 |
3 | **DeepLearning.scala** is a DSL for creating complex neural networks.
4 |
5 | With the help of DeepLearning.scala, regular programmers are able to build complex neural networks from simple code. You write code almost as usual, the only difference being that code based on DeepLearning.scala is [differentiable](https://colah.github.io/posts/2015-09-NN-Types-FP/), which enables such code to evolve by modifying its parameters continuously.
6 |
7 |
8 |
9 | ## NOTE: Please do NOT download this repo,because if you download this repo ,you will find the notebooks dir is empty. So ,please use git clone ,after that , use `git submodule init` & `git submodule update` to clone the notebooks(git submodule).
10 |
11 | ### [Jupyter notebook installation](https://github.com/alexarchambault/jupyter-scala)
12 |
13 | ## Tutorial index
14 |
15 | ### 1 - Getting Start
16 | - [getting start](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/GettingStarted.ipynb)
17 | - [how to debug](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/Debug.ipynb)
18 | - [softmax](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/SoftmaxLinearClassifier.ipynb)
19 | - [mini-batch gradient descent](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/MiniBatchGradientDescent.ipynb)
20 | - [two layer net](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/TwoLayerNet.ipynb)
21 | - [CNNs](https://github.com/thoughtworksinc/deeplearning.scala-website/blob/v1.0.0-doc/ipynbs/CNNs.ipynb)
22 |
23 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | global:
3 | COMPILE: compile
4 | TEST_COMPILE: test:compile
5 | IT_TEST_COMPILE: it:compile
6 | TEST_SUITE: test
7 | IT_TEST_SUITE: "it:testOnly -- -l NoWindows"
8 | matrix:
9 | # - SCALA_VERSION: 2.10.6
10 | - SCALA_VERSION: 2.11.11
11 | # - SCALA_VERSION: 2.12.2
12 | install:
13 | - ps: |
14 | Add-Type -AssemblyName System.IO.Compression.FileSystem
15 | if (!(Test-Path -Path "C:\sbt\sbt-launcher-packaging-0.13.13" )) {
16 | (new-object System.Net.WebClient).DownloadFile(
17 | 'https://dl.bintray.com/sbt/native-packages/sbt/0.13.13/sbt-0.13.13.zip',
18 | 'C:\sbt-0.13.13.zip'
19 | )
20 | [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\sbt-0.13.13.zip", "C:\sbt")
21 | }
22 | - ps: |
23 | if (!(Test-Path -Path "C:\Program Files\Java\jdk1.8.0\src.zip")) {
24 | (new-object System.Net.WebClient).DownloadFile(
25 | 'https://github.com/fommil/openjdk8src/raw/master/src.zip',
26 | 'C:\Program Files\Java\jdk1.8.0\src.zip'
27 | )
28 | }
29 | - cmd: SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0
30 | - cmd: SET PATH=C:\sbt\sbt-launcher-packaging-0.13.13\bin;%JAVA_HOME%\bin;%PATH%
31 | - cmd: SET COURSIER_VERBOSITY=-1
32 | - cmd: SET SBT_OPTS=-Xss2m -Xms1024m -Xmx4096m -XX:+TieredCompilation -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:MaxPermSize=256m -XX:ReservedCodeCacheSize=256M
33 | - cmd: SET SCALATEST_SPAN_SCALE_FACTOR=20
34 | - cmd: SET SBT_TASK_LIMIT=2
35 | - cmd: SET SCALATEST_PARALLEL_TESTS=2
36 | - cmd: SET SCALATEST_SLEEP_SCALE_FACTOR=2.0
37 | build_script:
38 | - sbt ";++ %SCALA_VERSION% %COMPILE%"
39 | # - sbt ";+++ %SCALA_VERSION% %COMPILE% ;+++ %SCALA_VERSION% %TEST_COMPILE% ;+++ %SCALA_VERSION% %IT_TEST_COMPILE%"
40 | # test_script:
41 | # - sbt ";+++ %SCALA_VERSION% %TEST_SUITE% ;+++ %SCALA_VERSION% %IT_TEST_SUITE%"
42 | cache:
43 | - C:\Program Files\Java\jdk1.8.0\src.zip
44 | - C:\sbt\sbt-launcher-packaging-0.13.13
45 | - C:\Users\appveyor\.ivy2 -> appveyor.yml
46 | - C:\Users\appveyor\.coursier -> appveyor.yml
47 | on_finish:
48 | - ps: Get-ChildItem -Path .\* -Include *.log,*.hprof -Recurse | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
49 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | name := "deeplearning-tutorial"
2 |
3 | version := "2.0"
4 |
5 | scalaVersion in Global := "2.11.11"
6 |
7 | val deepLearningScalaVersion = "2.0.0-M2"
8 |
9 | libraryDependencies += "com.thoughtworks.deeplearning" %% "differentiable" % deepLearningScalaVersion
10 |
11 | libraryDependencies += "org.plotly-scala" %% "plotly-render" % "0.3.2"
12 |
13 | libraryDependencies += "org.nd4j" % "nd4j-native-platform" % "0.7.2"
14 |
15 | libraryDependencies += "org.nd4j" % "nd4j-cuda-8.0-platform" % "0.7.2"
16 |
17 | libraryDependencies += "org.rauschig" % "jarchivelib" % "0.5.0"
18 |
19 | libraryDependencies += "org.slf4j" % "jul-to-slf4j" % "1.7.25"
20 |
21 | libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.25"
22 |
23 | libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.2"
24 |
25 | libraryDependencies += "ch.qos.logback" % "logback-core" % "1.2.2"
26 |
27 | addCompilerPlugin(
28 | "org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
29 |
30 | fork := true
31 |
--------------------------------------------------------------------------------
/cover.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/izhangzhihao/deeplearning-tutorial/bbce4e1aeeada61f916ce76248caecdbffe9a910/cover.jpeg
--------------------------------------------------------------------------------
/local.sbt:
--------------------------------------------------------------------------------
1 | scalaVersion in Global := "2.11.11"
2 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.15
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
--------------------------------------------------------------------------------
/src/main/resources/get_datasets.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Get CIFAR10
3 | wget http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz
4 | tar -xzvf cifar-10-binary.tar.gz
5 | rm cifar-10-binary.tar.gz
6 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/GettingStarted.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import com.thoughtworks.deeplearning.math._
4 | import com.thoughtworks.deeplearning.differentiable.Any._
5 | import com.thoughtworks.deeplearning.differentiable.INDArray.{
6 | Optimizer => INDArrayOptimizer
7 | }
8 | import INDArrayOptimizer.LearningRate
9 | import com.thoughtworks.deeplearning.differentiable.INDArray.implicits._
10 | import com.thoughtworks.each.Monadic._
11 | import com.thoughtworks.raii.asynchronous.Do
12 | import com.thoughtworks.deeplearning.differentiable.Double._
13 | import com.thoughtworks.deeplearning.differentiable.Double.implicits._
14 | import com.thoughtworks.deeplearning.{Tape, differentiable}
15 | import org.nd4j.linalg.api.ndarray.INDArray
16 | import org.nd4j.linalg.factory.Nd4j
17 | import org.nd4s.Implicits._
18 |
19 | import scala.concurrent.ExecutionContext.Implicits.global
20 | import scalaz.concurrent.Task
21 | import scalaz.{-\/, \/, \/-}
22 | import scalaz.std.vector._
23 |
24 | import plotly.Scatter
25 | import plotly.Plotly._
26 |
27 | object GettingStarted extends App {
28 |
29 | implicit def optimizer: INDArrayOptimizer = new LearningRate {
30 | def currentLearningRate() = 0.001
31 | }
32 |
33 | val weight = (Nd4j.randn(3, 1) / scala.math.sqrt(3.0)).toWeight
34 |
35 | def myNeuralNetwork(input: INDArray): differentiable.INDArray = {
36 | dot(input, weight)
37 | }
38 |
39 | def lossFunction(input: INDArray,
40 | expectOutput: INDArray): differentiable.Double = {
41 | sumT(abs(myNeuralNetwork(input) - expectOutput))
42 | }
43 |
44 | val input: INDArray =
45 | Array(Array(0, 1, 2), Array(3, 6, 9), Array(13, 15, 17)).toNDArray
46 |
47 | val expectedOutput: INDArray = Array(Array(1), Array(3), Array(2)).toNDArray
48 |
49 | @monadic[Task]
50 | val trainTask: Task[Unit] = {
51 |
52 | val lossSeq = for (_ <- (1 to 400).toVector) yield {
53 | train(lossFunction(input, expectedOutput)).each
54 | }
55 |
56 | polyLoss(lossSeq)
57 |
58 | }
59 |
60 | def polyLoss(lossSeq: IndexedSeq[Double]) = {
61 | val plot = Seq(
62 | Scatter(lossSeq.indices, lossSeq)
63 | )
64 |
65 | plot.plot(
66 | title = "loss by time"
67 | )
68 | }
69 |
70 | val predictResult = throwableMonadic[Task] {
71 | trainTask.each
72 | predict(myNeuralNetwork(input)).each
73 | }
74 |
75 | predictResult.unsafePerformSyncAttempt match {
76 | case -\/(e) => throw e
77 | case \/-(result) =>
78 | println(result)
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/MiniBatchGradientDescent.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import com.thoughtworks.deeplearning.math._
4 | import com.thoughtworks.deeplearning.differentiable.Any._
5 | import com.thoughtworks.deeplearning.differentiable.INDArray.{
6 | Optimizer => INDArrayOptimizer
7 | }
8 | import INDArrayOptimizer.LearningRate
9 | import com.github.izhangzhihao.GettingStarted.polyLoss
10 | import com.thoughtworks.deeplearning.differentiable.INDArray.implicits._
11 | import com.thoughtworks.each.Monadic._
12 | import com.thoughtworks.raii.asynchronous.Do
13 | import com.thoughtworks.deeplearning.differentiable.Double._
14 | import com.thoughtworks.deeplearning.differentiable.Double.implicits._
15 | import com.thoughtworks.deeplearning.{Tape, differentiable}
16 | import org.nd4j.linalg.api.ndarray.INDArray
17 | import org.nd4j.linalg.factory.Nd4j
18 | import org.nd4s.Implicits._
19 |
20 | import scala.concurrent.ExecutionContext.Implicits.global
21 | import scalaz.concurrent.Task
22 | import scalaz.{-\/, \/, \/-}
23 | import scalaz.std.vector._
24 | import scalaz.std.list._
25 | import scalaz.std.map._
26 | import scalaz.std.iterable._
27 | import plotly.Scatter
28 | import plotly.Plotly._
29 | import shapeless._
30 |
31 | import scala.util.Random
32 |
33 | /**
34 | * @author 张志豪 (izhangzhihao) <izhangzhihao@hotmail.com>
35 | */
36 | object MiniBatchGradientDescent extends App {
37 | //CIFAR10中的图片共有10个分类(airplane,automobile,bird,cat,deer,dog,frog,horse,ship,truck)
38 | val NumberOfClasses: Int = 10
39 | val NumberOfPixels: Int = 3072
40 |
41 | //加载测试数据,我们读取100条作为测试数据
42 | val testNDArray =
43 | ReadCIFAR10ToNDArray.readFromResource(
44 | "/cifar-10-batches-bin/test_batch.bin",
45 | 100)
46 |
47 | val testData = testNDArray.head
48 | val testExpectResult = testNDArray.tail.head
49 |
50 | val vectorizedTestExpectResult =
51 | Utils.makeVectorized(testExpectResult, NumberOfClasses)
52 |
53 | def softmax(scores: differentiable.INDArray): differentiable.INDArray = {
54 | val expScores = exp(scores)
55 | expScores / sum(expScores, 1)
56 | }
57 |
58 | implicit def optimizer: INDArrayOptimizer = new LearningRate {
59 | def currentLearningRate() = 0.00001
60 | }
61 |
62 | val weight: differentiable.INDArray =
63 | (Nd4j.randn(NumberOfPixels, NumberOfClasses) * 0.001).toWeight
64 |
65 | def myNeuralNetwork(input: INDArray): differentiable.INDArray = {
66 | softmax(dot(input, weight))
67 | }
68 |
69 | def lossFunction(input: INDArray,
70 | expectOutput: INDArray): differentiable.Double = {
71 | val probabilities = myNeuralNetwork(input)
72 | -mean(log(probabilities) * expectOutput)
73 | }
74 |
75 | @monadic[Task]
76 | val trainTask: Task[Unit] = {
77 | val random = new Random
78 |
79 | val MiniBatchSize = 256
80 |
81 | val lossSeq =
82 | (
83 | for (_ <- (0 to 50).toVector) yield {
84 | val randomIndex = random
85 | .shuffle[Int, IndexedSeq](0 until 10000) //https://issues.scala-lang.org/browse/SI-6948
86 | .toArray
87 | for (times <- (0 until 10000 / MiniBatchSize).toVector) yield {
88 | val randomIndexArray =
89 | randomIndex.slice(times * MiniBatchSize,
90 | (times + 1) * MiniBatchSize)
91 | val trainNDArray :: expectLabel :: shapeless.HNil =
92 | ReadCIFAR10ToNDArray.getSGDTrainNDArray(randomIndexArray)
93 | val input =
94 | trainNDArray.reshape(MiniBatchSize, 3072)
95 |
96 | val expectLabelVectorized =
97 | Utils.makeVectorized(expectLabel, NumberOfClasses)
98 | val loss = train(lossFunction(input, expectLabelVectorized)).each
99 | println(loss)
100 | loss
101 | }
102 | }
103 | ).flatten
104 |
105 | polyLoss(lossSeq)
106 | }
107 |
108 | val predictResult = throwableMonadic[Task] {
109 | trainTask.each
110 | predict(myNeuralNetwork(testData)).each
111 | }
112 |
113 | predictResult.unsafePerformSyncAttempt match {
114 | case -\/(e) => throw e
115 | case \/-(result) =>
116 | println(
117 | "The accuracy is " + Utils.getAccuracy(result, testExpectResult) + "%")
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/ReadCIFAR10.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import java.awt.Color
4 | import java.awt.image.BufferedImage
5 | import java.io.FileOutputStream
6 | import javax.imageio.ImageIO
7 |
8 | /**
9 | * Created by 张志豪 on 1/16/17.
10 | * 显示一个CIFAR10中的图片
11 | */
12 | object ReadCIFAR10 extends App {
13 | private val inputStream =
14 | getClass.getResourceAsStream("/cifar-10-batches-bin/data_batch_1.bin")
15 | private val bytes = Array.range(0, 3073).map(_.toByte)
16 | inputStream.read(bytes)
17 |
18 | private val bufferedImage =
19 | new BufferedImage(32, 32, BufferedImage.TYPE_INT_RGB)
20 |
21 | for (row <- 0 until 32) {
22 | for (col <- 0 until 32) {
23 | val color = new Color(bytes(1 + 1024 * 0 + row * 32 + col) & 0xFF,
24 | bytes(1 + 1024 * 1 + row * 32 + col) & 0xFF,
25 | bytes(1 + 1024 * 2 + row * 32 + col) & 0xFF)
26 | bufferedImage.setRGB(col, row, color.getRGB)
27 | }
28 | }
29 |
30 | ImageIO.write(bufferedImage, "jpeg", new FileOutputStream("./out.jpg"))
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/ReadCIFAR10ToNDArray.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import java.io._
4 | import java.net.URL
5 | import java.util.zip.GZIPInputStream
6 |
7 | import sys.process._
8 | import org.nd4j.linalg.api.ndarray.INDArray
9 | import org.nd4j.linalg.factory.Nd4j
10 | import org.nd4s.Implicits._
11 | import org.rauschig.jarchivelib.{Archiver, ArchiverFactory}
12 | import shapeless._
13 |
14 | import scala.collection.immutable.IndexedSeq
15 |
16 | /**
17 | * Created by 张志豪 on 1/16/17.
18 | */
19 | object ReadCIFAR10ToNDArray {
20 |
21 | /**
22 | * 原始文件字节
23 | */
24 | lazy val originalFileBytesArray: Array[Array[Byte]] = {
25 | for (fileIndex <- 1 to 5) yield {
26 |
27 | val currentPath = new java.io.File(".").getCanonicalPath + "/src/main/resources/"
28 |
29 | val fileName = currentPath + "/cifar-10-batches-bin" + "/data_batch_" + fileIndex + ".bin"
30 |
31 | if (!new File(fileName).exists()) {
32 | downloadDataAndUnzipIfNotExist(
33 | "http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz",
34 | currentPath,
35 | "cifar-10-binary.tar.gz"
36 | )
37 | }
38 | val inputStream = new FileInputStream(fileName)
39 | readFromInputStream(inputStream)
40 | }
41 | }.toArray
42 |
43 | /**
44 | * 中心化过后的图片
45 | */
46 | lazy val pixelBytesArray: Array[Array[Array[Double]]] = {
47 | for (fileIndex <- 0 until 5) yield {
48 | val originalFileBytes = originalFileBytesArray(fileIndex)
49 | for (index <- 0 until 10000) yield {
50 | val beginIndex = index * 3073 + 1
51 | normalizePixel(originalFileBytes.slice(beginIndex, beginIndex + 3072))
52 | }
53 | }.toArray
54 | }.toArray
55 |
56 | /**
57 | * 图片对应的label
58 | */
59 | lazy val labelBytesArray: Array[Array[Int]] = {
60 | for (fileIndex <- 0 until 5) yield {
61 | val originalFileBytes = originalFileBytesArray(fileIndex)
62 | for (index <- 0 until 10000) yield {
63 | val beginIndex = index * 3073
64 | originalFileBytes(beginIndex).toInt
65 | }
66 | }.toArray
67 | }.toArray
68 |
69 | val random = new util.Random
70 |
71 | /**
72 | * 从inputStream中读取byte
73 | *
74 | * @param inputStream
75 | * @return
76 | */
77 | def readFromInputStream(inputStream: InputStream): Array[Byte] = {
78 | try {
79 | val bytes = Array.range(0, 3073 * 10000).map(_.toByte)
80 | inputStream.read(bytes)
81 | bytes
82 | } finally {
83 | inputStream.close()
84 | }
85 | }
86 |
87 | /**
88 | * 数据集不存在的话下载并解压
89 | *
90 | * @param path
91 | * @param url
92 | * @param fileName
93 | */
94 | //noinspection ScalaUnusedSymbol
95 | def downloadDataAndUnzipIfNotExist(url: String,
96 | path: String,
97 | fileName: String): Unit = {
98 | println("downloading CIFAR10 database...")
99 | val result = new URL(url) #> new File(path + fileName) !!
100 |
101 | println("unzip CIFAR10 database...")
102 | val archiver: Archiver = ArchiverFactory.createArchiver("tar", "gz")
103 | archiver.extract(new File(path + fileName), new File(path))
104 | println("download and unzip done.")
105 | }
106 |
107 | /**
108 | * 从CIFAR10文件中读图片和其对应的标签
109 | *
110 | * @param fileName CIFAR10文件名
111 | * @param count 要读取多少个图片和其标签
112 | * @return input :: expectedOutput :: HNil
113 | */
114 | def readFromResource(fileName: String,
115 | count: Int): INDArray :: INDArray :: HNil = {
116 |
117 | val currentPath = new java.io.File(".").getCanonicalPath + "/src/main/resources/"
118 |
119 | val filePathName = currentPath + fileName
120 |
121 | if (!new File(filePathName).exists()) {
122 | downloadDataAndUnzipIfNotExist(
123 | "http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz",
124 | currentPath,
125 | "cifar-10-binary.tar.gz"
126 | )
127 | }
128 |
129 | val inputStream = new FileInputStream(filePathName)
130 | try {
131 | val bytes = Array.range(0, 3073 * count).map(_.toByte)
132 | inputStream.read(bytes)
133 |
134 | val labels: Seq[Double] =
135 | for (index <- 0 until count) yield {
136 | bytes(index * 3073).toDouble
137 | }
138 |
139 | val pixels: Seq[Seq[Double]] =
140 | for (index <- 0 until count) yield {
141 | for (item <- 1 until 3073) yield {
142 | normalizePixel(bytes(index * 3073 + item).toDouble)
143 | }
144 | }
145 |
146 | val labelsArray = labels.toNDArray.reshape(count, 1)
147 | val pixelsArray = pixels.toNDArray
148 |
149 | pixelsArray :: labelsArray :: HNil
150 | } finally {
151 | inputStream.close()
152 | }
153 | }
154 |
155 | /**
156 | * 归一化pixel数据
157 | *
158 | * @param pixel
159 | * @return
160 | */
161 | def normalizePixel(pixel: Double): Double = {
162 | (if (pixel < 0) {
163 | pixel + 256
164 | } else {
165 | pixel
166 | }) / 256
167 | }
168 |
169 | /**
170 | * 归一化数组的pixel数据
171 | *
172 | * @param original
173 | * @return
174 | */
175 | def normalizePixel(original: Array[Byte]): Array[Double] = {
176 | for (pixel <- original) yield {
177 | normalizePixel(pixel)
178 | }
179 | }
180 |
181 | /**
182 | * 随机获取count个train数据
183 | *
184 | * @return
185 | */
186 | def getSGDTrainNDArray(
187 | randomIndexArray: Array[Int]): INDArray :: INDArray :: HNil = {
188 | //生成0到4的随机数
189 | val randomIndex = random.nextInt(5)
190 | val labelBytes = labelBytesArray(randomIndex)
191 | val pixelBytes = pixelBytesArray(randomIndex)
192 | val count = randomIndexArray.length
193 |
194 | val labels: Seq[Int] =
195 | for (index <- 0 until count) yield {
196 | labelBytes(randomIndexArray(index))
197 | }
198 |
199 | val pixels: Seq[Seq[Double]] =
200 | for (index <- 0 until count) yield {
201 | pixelBytes(randomIndexArray(index)).toList
202 | }
203 |
204 | val labelsNDArray = labels.toNDArray.reshape(count, 1)
205 | val pixelsNDArray = pixels.toNDArray
206 |
207 | pixelsNDArray :: labelsNDArray :: HNil
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/SoftmaxLinearClassifier.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import com.thoughtworks.deeplearning.math._
4 | import com.thoughtworks.deeplearning.differentiable.Any._
5 | import com.thoughtworks.deeplearning.differentiable.INDArray.{
6 | Optimizer => INDArrayOptimizer
7 | }
8 | import INDArrayOptimizer.LearningRate
9 | import com.github.izhangzhihao.GettingStarted.polyLoss
10 | import com.thoughtworks.deeplearning.differentiable.INDArray.implicits._
11 | import com.thoughtworks.each.Monadic._
12 | import com.thoughtworks.raii.asynchronous.Do
13 | import com.thoughtworks.deeplearning.differentiable.Double._
14 | import com.thoughtworks.deeplearning.differentiable.Double.implicits._
15 | import com.thoughtworks.deeplearning.{Tape, differentiable}
16 | import org.nd4j.linalg.api.ndarray.INDArray
17 | import org.nd4j.linalg.factory.Nd4j
18 | import org.nd4s.Implicits._
19 |
20 | import scala.concurrent.ExecutionContext.Implicits.global
21 | import scalaz.concurrent.Task
22 | import scalaz.{-\/, \/, \/-}
23 | import scalaz.std.vector._
24 | import plotly.Scatter
25 | import plotly.Plotly._
26 |
27 | /**
28 | * @author 张志豪 (izhangzhihao) <izhangzhihao@hotmail.com>
29 | */
30 | object SoftmaxLinearClassifier extends App {
31 |
32 | //CIFAR10中的图片共有10个分类(airplane,automobile,bird,cat,deer,dog,frog,horse,ship,truck)
33 | val NumberOfClasses: Int = 10
34 | val NumberOfPixels: Int = 3072
35 |
36 | //加载train数据,我们读取1000条数据作为训练数据
37 | val trainNDArray =
38 | ReadCIFAR10ToNDArray.readFromResource(
39 | "/cifar-10-batches-bin/data_batch_1.bin",
40 | 1000)
41 |
42 | //加载测试数据,我们读取100条作为测试数据
43 | val testNDArray =
44 | ReadCIFAR10ToNDArray.readFromResource(
45 | "/cifar-10-batches-bin/test_batch.bin",
46 | 100)
47 |
48 | val trainData = trainNDArray.head
49 | val testData = testNDArray.head
50 |
51 | val trainExpectResult = trainNDArray.tail.head
52 | val testExpectResult = testNDArray.tail.head
53 |
54 | val vectorizedTrainExpectResult =
55 | Utils.makeVectorized(trainExpectResult, NumberOfClasses)
56 | val vectorizedTestExpectResult =
57 | Utils.makeVectorized(testExpectResult, NumberOfClasses)
58 |
59 | def softmax(scores: differentiable.INDArray): differentiable.INDArray = {
60 | val expScores = exp(scores)
61 | expScores / sum(expScores, 1)
62 | }
63 |
64 | implicit def optimizer: INDArrayOptimizer = new LearningRate {
65 | def currentLearningRate() = 0.00001
66 | }
67 |
68 | val weight: differentiable.INDArray =
69 | (Nd4j.randn(NumberOfPixels, NumberOfClasses) * 0.001).toWeight
70 |
71 | def myNeuralNetwork(input: INDArray): differentiable.INDArray = {
72 | softmax(dot(input, weight))
73 | }
74 |
75 | def lossFunction(input: INDArray,
76 | expectOutput: INDArray): differentiable.Double = {
77 | val probabilities = myNeuralNetwork(input)
78 | -mean(log(probabilities) * expectOutput)
79 | }
80 |
81 | @monadic[Task]
82 | val trainTask: Task[Unit] = {
83 |
84 | val lossSeq = for (_ <- (1 to 2000).toVector) yield {
85 | val loss =
86 | train(lossFunction(trainData, vectorizedTrainExpectResult)).each
87 | println(loss)
88 | loss
89 | }
90 |
91 | polyLoss(lossSeq)
92 | }
93 |
94 | val predictResult = throwableMonadic[Task] {
95 | trainTask.each
96 | predict(myNeuralNetwork(testData)).each
97 | }
98 |
99 | predictResult.unsafePerformSyncAttempt match {
100 | case -\/(e) => throw e
101 | case \/-(result) =>
102 | println(
103 | "The accuracy is " + Utils.getAccuracy(result, testExpectResult) + "%")
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/TwoLayerNet.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import com.thoughtworks.deeplearning.math._
4 | import com.thoughtworks.deeplearning.differentiable.Any._
5 | import com.thoughtworks.deeplearning.differentiable.INDArray.{Optimizer => INDArrayOptimizer, Weight => INDArrayWeight}
6 | import INDArrayOptimizer.{L2Regularization, LearningRate}
7 | import com.github.izhangzhihao.GettingStarted.polyLoss
8 | import com.thoughtworks.deeplearning.Lift
9 | import com.thoughtworks.deeplearning.Tape
10 | import com.thoughtworks.deeplearning.differentiable.INDArray.implicits._
11 | import com.thoughtworks.each.Monadic._
12 | import com.thoughtworks.raii.asynchronous
13 | import com.thoughtworks.raii.asynchronous.Do
14 | import com.thoughtworks.raii.ownership.Borrowing
15 | import org.nd4j.linalg.api.ndarray.INDArray
16 | import com.thoughtworks.deeplearning.differentiable.Double.implicits._
17 | import com.thoughtworks.deeplearning.{Tape, differentiable}
18 | import org.nd4j.linalg.api.ndarray.INDArray
19 | import org.nd4j.linalg.factory.Nd4j
20 | import org.nd4s.Implicits._
21 |
22 | import scala.concurrent.ExecutionContext.Implicits.global
23 | import scalaz.concurrent.Task
24 | import scalaz.{-\/, \/, \/-}
25 | import scalaz.std.vector._
26 | import scalaz.std.list._
27 | import scalaz.std.map._
28 | import scalaz.std.iterable._
29 | import plotly.Scatter
30 | import plotly.Plotly._
31 | import shapeless._
32 |
33 | import scala.util.Random
34 |
35 | /**
36 | * @author 张志豪 (izhangzhihao) <izhangzhihao@hotmail.com>
37 | */
38 | object TwoLayerNet extends App {
39 | //CIFAR10中的图片共有10个分类(airplane,automobile,bird,cat,deer,dog,frog,horse,ship,truck)
40 | val NumberOfClasses: Int = 10
41 |
42 | //加载测试数据,我们读取100条作为测试数据
43 | val testNDArray =
44 | ReadCIFAR10ToNDArray.readFromResource(
45 | "/cifar-10-batches-bin/test_batch.bin",
46 | 100)
47 |
48 | val testData = testNDArray.head
49 | val testExpectResult = testNDArray.tail.head
50 |
51 | val vectorizedTestExpectResult =
52 | Utils.makeVectorized(testExpectResult, NumberOfClasses)
53 |
54 | implicit val optimizerFactory =
55 | new differentiable.INDArray.OptimizerFactory {
56 | override def indarrayOptimizer(
57 | weight: INDArrayWeight): INDArrayOptimizer = {
58 | new LearningRate with L2Regularization {
59 |
60 | var learningRate = 0.01
61 |
62 | override protected def currentLearningRate(): Double = {
63 | learningRate *= 0.9995
64 | learningRate
65 | }
66 |
67 | override protected def l2Regularization: Double = 0.03
68 | }
69 | }
70 | }
71 |
72 | def fullyConnectedThenRelu(inputSize: Int,
73 | outputSize: Int,
74 | input: differentiable.INDArray) = {
75 | //TODO
76 | val weight =
77 | (Nd4j.randn(inputSize, outputSize) / math.sqrt(outputSize / 2.0)).toWeight * 0.1
78 | val b = Nd4j.zeros(outputSize).toWeight
79 | max(dot(input, weight) + b, 0.0)
80 | }
81 |
82 | def softmax(scores: differentiable.INDArray): differentiable.INDArray = {
83 | val expScores = exp(scores)
84 | expScores / sum(expScores, 1)
85 | }
86 |
87 | def fullyConnectedThenSoftmax(
88 | inputSize: Int,
89 | outputSize: Int,
90 | input: differentiable.INDArray): differentiable.INDArray = {
91 | //TODO
92 | val weight =
93 | (Nd4j.randn(inputSize, outputSize) / math.sqrt(outputSize)).toWeight
94 | val b = Nd4j.zeros(outputSize).toWeight
95 | softmax(dot(input, weight) + b)
96 | }
97 |
98 | val NumberOfPixels: Int = 3072
99 |
100 | def myNeuralNetwork(
101 | input: differentiable.INDArray): differentiable.INDArray = {
102 |
103 | val layer0 = fullyConnectedThenRelu(NumberOfPixels, 500, input)
104 |
105 | fullyConnectedThenSoftmax(500, 10, layer0)
106 | }
107 |
108 | def crossEntropy(score: differentiable.INDArray,
109 | label: differentiable.INDArray): differentiable.Double = {
110 | -mean(
111 | label * log(score * 0.9 + 0.1) + (1.0 - label) * log(1.0 - score * 0.9))
112 | }
113 |
114 | def lossFunction(input: INDArray,
115 | expectOutput: INDArray)(implicit liftINDArray: Lift.Aux[INDArray, INDArray, INDArray]): differentiable.Double = {
116 |
117 | val score
118 | : differentiable.INDArray = myNeuralNetwork(1.0 * input) // TODO
119 | crossEntropy(score, 1.0 * expectOutput) //TODO
120 | }
121 |
122 | @monadic[Task]
123 | val trainTask: Task[Unit] = {
124 | val random = new Random
125 |
126 | val MiniBatchSize = 256
127 |
128 | val lossSeq =
129 | (
130 | for (_ <- (0 to 50).toVector) yield {
131 | val randomIndex = random
132 | .shuffle[Int, IndexedSeq](0 until 10000) //https://issues.scala-lang.org/browse/SI-6948
133 | .toArray
134 | for (times <- (0 until 10000 / MiniBatchSize).toVector) yield {
135 | val randomIndexArray =
136 | randomIndex.slice(times * MiniBatchSize,
137 | (times + 1) * MiniBatchSize)
138 | val trainNDArray :: expectLabel :: shapeless.HNil =
139 | ReadCIFAR10ToNDArray.getSGDTrainNDArray(randomIndexArray)
140 | val input =
141 | trainNDArray.reshape(MiniBatchSize, 3072)
142 |
143 | val expectLabelVectorized =
144 | Utils.makeVectorized(expectLabel, NumberOfClasses)
145 | val loss = train(lossFunction(input, expectLabelVectorized)).each
146 | println(loss)
147 | loss
148 | }
149 | }
150 | ).flatten
151 |
152 | polyLoss(lossSeq)
153 | }
154 |
155 | val predictResult = throwableMonadic[Task] {
156 | trainTask.each
157 |
158 | predict(myNeuralNetwork(1.0 * testData)).each //TODO
159 | }
160 |
161 | predictResult.unsafePerformSyncAttempt match {
162 | case -\/(e) => throw e
163 | case \/-(result) =>
164 | println(Utils.getAccuracy(result, testExpectResult))
165 | }
166 |
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/scala/com/github/izhangzhihao/Utils.scala:
--------------------------------------------------------------------------------
1 | package com.github.izhangzhihao
2 |
3 | import org.nd4j.linalg.api.ndarray.INDArray
4 | import org.nd4j.linalg.factory.Nd4j
5 | import org.nd4s.Implicits._
6 |
7 | import scala.collection.GenTraversable
8 | import scala.collection.generic.GenericTraversableTemplate
9 | import scala.reflect.ClassTag
10 |
11 | /**
12 | * Created by 张志豪 on 2017/2/22.
13 | */
14 | object Utils {
15 |
16 | /**
17 | * 处理标签数据:将N行一列的NDArray转换为N行NumberOfClasses列的NDArray,每行对应的正确分类的值为1,其它列的值为0
18 | *
19 | * @param ndArray 标签数据
20 | * @return N行NumberOfClasses列的NDArray
21 | */
22 | def makeVectorized(ndArray: INDArray, numberOfClasses: Int): INDArray = {
23 | val shape = ndArray.shape()
24 |
25 | val p = Nd4j.zeros(shape(0), numberOfClasses)
26 | for (i <- 0 until shape(0)) {
27 | val double = ndArray.getDouble(i, 0)
28 | val column = double.toInt
29 | p.put(i, column, 1)
30 | }
31 | p
32 | }
33 |
34 | /**
35 | * 二维INDArray中获得值最大的元素所在的列组成的INDArray
36 | *
37 | * @param iNDArray
38 | * @return
39 | */
40 | def findMaxItemIndex(iNDArray: INDArray): INDArray = {
41 | Nd4j.argMax(iNDArray, 1)
42 | }
43 |
44 | /**
45 | * 计算准确率
46 | * @param score 预测结果
47 | * @param testExpectLabel 期望结果
48 | * @return 准确率
49 | */
50 | def getAccuracy(score: INDArray, testExpectLabel: INDArray): Double = {
51 | val scoreIndex = findMaxItemIndex(score)
52 | if (testExpectLabel.shape().toSeq.last == 1) { //not vectorized
53 | val acc = for (row <- 0 until scoreIndex.shape()(0)) yield {
54 | if (scoreIndex.getDouble(row, 0) ==
55 | testExpectLabel.getDouble(row, 0)) {
56 | 1.0
57 | } else 0.0
58 | }
59 | (acc.sum / score.shape()(0)) * 100
60 | } else if (testExpectLabel.shape().toSeq.last == 10) { //vectorized
61 | val expectResultIndex = findMaxItemIndex(testExpectLabel)
62 | val accINDArray = scoreIndex.eq(expectResultIndex)
63 | (accINDArray.sumT / score.shape()(0)) * 100
64 | } else
65 | throw new IllegalArgumentException("Unacceptable testExpectLabel")
66 | }
67 |
68 | class Unzipper[A, CC[X] <: GenTraversable[X]](
69 | s: GenericTraversableTemplate[A, CC]) {
70 | def unzip4[A1, A2, A3, A4](implicit asQuad: A => (A1, A2, A3, A4))
71 | : (CC[A1], CC[A2], CC[A3], CC[A4]) = {
72 | val b1 = s.genericBuilder[A1]
73 | val b2 = s.genericBuilder[A2]
74 | val b3 = s.genericBuilder[A3]
75 | val b4 = s.genericBuilder[A4]
76 | for (e <- s) {
77 | val (a, b, c, d) = asQuad(e)
78 | b1 += a
79 | b2 += b
80 | b3 += c
81 | b4 += d
82 | }
83 | (b1.result, b2.result, b3.result, b4.result)
84 | }
85 | }
86 |
87 | implicit def toUnzipper[A, CC[X] <: GenTraversable[X]](
88 | s: GenericTraversableTemplate[A, CC]): Unzipper[A, CC] = new Unzipper(s)
89 |
90 | class Tuple2ToArray[A: ClassTag](tuple: (A, A)) {
91 | def toArray: Array[A] = {
92 | val (one: A, two: A) = tuple
93 | Array(one, two)
94 | }
95 | }
96 |
97 | implicit def tupleTwoToArray[A: ClassTag](tuple: (A, A)): Tuple2ToArray[A] =
98 | new Tuple2ToArray[A](tuple)
99 |
100 | }
101 |
--------------------------------------------------------------------------------