強化学習の関数近似にニューラルネットワークを組合せるということをやってきていた。
強化学習については以下から:
ニューラルネットワークについては以下から:
複数のニューラルネットワークを組合せるHME(Hierarchical Mixtures of Experts)については以下から:
強化学習とニューラルネットワーク(HME含む)の組合せについては以下から:
この強化学習用のニューラルネットワークはRubyで書いてたけど、iOSアプリを作るとなると、Swiftに移植しないといけない。
ということで、Swiftに移植してみた。
乱数生成器
まず必要となるのが、乱数生成器。
一様乱数の生成器は以前も書いたけど、再掲。
//============================== // Random //------------------------------ // Random.swift //============================== import Foundation import Security class Random { static func getUniformedRandom(upperBound: Int) -> Int { let upper = UInt32(upperBound) let ignoreRegion = 0xffff_ffff - 0xffff_ffff % upper while true { let buffer = UnsafeMutablePointer<UInt32>.alloc(1) SecRandomCopyBytes(kSecRandomDefault, 4, UnsafeMutablePointer<UInt8>(buffer)) let randomValue = buffer.memory buffer.dealloc(1) if randomValue < ignoreRegion { return Int(randomValue % upper) } } } static func getRandomProbability() -> Double { return Double(Random.getUniformedRandom(0x1_0000)) / Double(0xffff) } private init() {} } extension Array { func sample() -> Element { let index = Random.getUniformedRandom(self.count) return self[index] } }
この一様乱数の生成器の説明は、以下から:
そして、ニューラルネットワークの重みの初期化で正規分布に従う乱数生成器も必要になるので、移植。
正規分布に従う乱数生成器の説明は、以下から:
実装は以下:
//============================== // Random //------------------------------ // NormalDistRandom.swift //============================== import Foundation class NormalDistRandom { private let expected: Double private let variance: Double private var values: [Double] init(expected: Double, variance: Double) { self.expected = expected self.variance = variance self.values = [] } func getRandom() -> Double { if self.values.count == 0 { var a = 0.0 var b = 0.0 repeat { a = Random.getRandomProbability() b = Random.getRandomProbability() } while (a == 0.0) || (a == 1.0) || (b == 0.0) || (b == 1.0) let z1 = sqrt(-2.0 * log(a)) * cos(2.0 * M_PI * b) let z2 = sqrt(-2.0 * log(a)) * sin(2.0 * M_PI * b) let rand1 = z1 * sqrt(self.variance) + self.expected let rand2 = z2 * sqrt(self.variance) + self.expected self.values = [rand1, rand2] } return self.values.removeLast() } }
乱数生成器の動作確認
これらの乱数生成器の動作確認として、以下のコードを書いた:
//============================== // Random //------------------------------ // main.swift // // Test code for Random //============================== import Foundation let count = 1000 // Random.getUniformedRandom() var valueCount = [ 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, ] for _ in 0..<count { let randomValue = Random.getUniformedRandom(10) valueCount[randomValue] = valueCount[randomValue]! + 1 } print("----------") print("Random.getUniformedRandom()") print("----------") for value in valueCount.keys.sort() { print("\(value): \(valueCount[value]!)") } // Random.getRandomProbability() var sum = 0.0 var squareSum = 0.0 var minValue = 100.0 var maxValue = -100.0 for i in 0..<count { let randomValue = Random.getRandomProbability() sum += randomValue squareSum += randomValue * randomValue if randomValue < minValue { minValue = randomValue } if maxValue < randomValue { maxValue = randomValue } } var mean = sum / Double(count) var variance = squareSum / Double(count) - mean * mean print("----------") print("Random.getRandomProbability()") print("----------") print("min: \(minValue)") print("max: \(maxValue)") print("mean: \(mean)") print("variance: \(variance)") // Array#sample() let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] valueCount = [ 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, ] for _ in 0..<count { let randomValue = array.sample() valueCount[randomValue] = valueCount[randomValue]! + 1 } print("----------") print("Array#sample()") print("----------") for value in valueCount.keys.sort() { print("\(value): \(valueCount[value]!)") } // NormalDistRandom let normalDistRandom = NormalDistRandom(expected: 1.0, variance: 2.0) sum = 0.0 squareSum = 0.0 minValue = 100.0 maxValue = -100.0 for i in 0..<count { let randomValue = normalDistRandom.getRandom() sum += randomValue squareSum += randomValue * randomValue if randomValue < minValue { minValue = randomValue } if maxValue < randomValue { maxValue = randomValue } } mean = sum / Double(count) variance = squareSum / Double(count) - mean * mean print("----------") print("normalDistRandom.getRanodm()") print("----------") print("min: \(minValue)") print("max: \(maxValue)") print("mean: \(mean)") print("variance: \(variance)")
実行例は、以下:
---------- Random.getUniformedRandom() ---------- 0: 98 1: 104 2: 100 3: 85 4: 105 5: 112 6: 117 7: 86 8: 103 9: 90 ---------- Random.getRandomProbability() ---------- min: 9.15541313801785e-05 max: 0.998825055313954 mean: 0.472300785839628 variance: 0.0830635419408054 ---------- Array#sample() ---------- 0: 90 1: 110 2: 100 3: 112 4: 98 5: 91 6: 101 7: 102 8: 106 9: 90 ---------- normalDistRandom.getRanodm() ---------- min: -3.76411577670718 max: 5.42937254648993 mean: 0.989138466448474 variance: 2.1180087175273
今日はここまで!