Each machine learning task is related with big amount of data. Analyzing a network is a complex and confusing task. To resolve that issue, Google announced launch of visualization tools called TensorBoard.
Currently that is the most useful source-code tool. Unfortunately that tool works only with TensorFlow library from the box. There is no way to feed it with json or xml logs.
Deepening into a self-written neural network you can’t avoid any data-visualization task. For that reason you can use Tensorboard from C/C++/Java or Swift application.
How to do that, I will describe further.
As you can see at Tensorflow GitHub repository, any event and summary essences use protobuff to serialize themselves in to stream data. Protobuff is a Google written protocol-buffers protocol. It is open source and there are lots of different extensions for different languages. For Swift, there is a realization in official Apple GitHub repository. That is how it works:
- Build and install a Swift Protobuff package;
- Generate a Swift structures from a Proto descriptors;
- There is event Proto descriptors at tensorflow repository;
- Feed the structures with your event data;
- Serialize the structures to a binary data;
- Merge all binary data to one file;
- Feed a TensorBoard with your log folder;
If you want, you can generate swift classes for all available proto schemes by few commands:
1 2 3 4 5 6 7 8 9 10 |
// Create temperory folder mkdir /tmp/swift cd %path-to-tensorflow-reposytory% // Find all proto files and generate swift classes. find. -name '*.proto' -print -exec protoc --swift_out=/tmp/swift {} \; // All files will be removed after restart. open /tmp/swift |
Also there is some trick, you have to merge your data with some header and footer fields. That fields have to contains some CRC32 checksum.
There is no only events available for representation, you can build graph, histograms, distribution and etc objects for visualization. You can see results at attached screens.
In result you can see pretty cross-platform Framework : TensorBoardKit wit Logger class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import CRC32 import Foundation import MemoryLayoutKit import SwiftOnoneSupport import SwiftProtobuf public class Logger { public init(folder url: URL) throws public func flush() throws public func track(tag: String, value: Float, time: TimeInterval, step: Int64) throws public func track(tag: String, values: [Double], time: TimeInterval, step: Int64) throws } public enum LoggerError : Error { case canNotComputeFileURL case canNotCreateFile } |
And simple use for that Logger:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import XCTest import Logger class LoggerTests: XCTestCase { func testCRC32() { guard let url = URL(string: "/tmp/log/") else { XCTFail("Can't compute folder url.") return } do { let logger = try Logger(folder: url) var distributions = [Double]() for _ in 0..<1000 { distributions.append(Double(UInt8.max) * Double(arc4random()) / Double(UINT32_MAX) - (Double(UInt8.max) / 2.0)) } for track in 0..<1000 { try logger.track(tag: "Distribution of W1", values: distributions.map {$0 * Double(track) * 1e-3}, time: Date().timeIntervalSince1970, step: Int64(track)) } for i in 0..<1000 { try logger.track(tag: "accuracy", value: sqrtf(Float(i)), time: Date().timeIntervalSince1970, step: Int64(i)) } for i in 0..<1000 { try logger.track(tag: "diff", value: sqrtf(Float(8 - Float(arc4random_uniform(UInt32(10))) + 4)), time: Date().timeIntervalSince1970, step: Int64(i)) } try logger.flush() } catch { XCTFail(error.localizedDescription) } } static var allTests : [(String, (LoggerTests) -> () throws -> Void)] { return [ ("testExample", testCRC32), ] } } |
Author: Volodymyr Pavliukevych
Senior Software Engineer, Data Scientist.