00_load_data Notebook Notes

Jeremy had asked me to look over the Swift notebooks and offer any suggestions. I know that previously there was a topic per notebook for some other things so I’d thought I’d put each in its own topic. Or could be just in this one if desired.

I downloaded and installed everything and so far have gone through the first notebook. Here are some suggestions. Swift introduces some new paradigms which I like, but you may not like to do it that way so these are just suggestions.

  1. There’s no need to explicitly use .init when creating objects. For example, change URL.init(…) to just URL(…) and Data.init(…) to Data(…), etc.

  2. The line _ = shellCommand("/bin/gunzip", ["-fq", (path/"(fname).gz").string]) is coded to ignore the return value. It may have been coded this way because Swift will give an error if a function returns a value that isn’t used or specifically ignored. You can however, mark the shellCommand function with the @discardableResult annotation instead and then just have the line as shellCommand("/bin/gunzip", ["-fq", (path/"(fname).gz").string])

  3. You have 2 different time functions that are only slightly different. I’m not sure if this is done for teaching purposes or not but you can make it into just one function by adding a default value of = 1 to the repeating parameter. Also, if the priming function() call should only be invoked when doing more than 1 time, you can put an “if” statement around that (shown below). Also, I changed var times: [Double] = [] to var times = [Double]()

    public func time(repeating: Int = 1, _ function: () -> ()) {
        if repeating > 1 { function() }
        var times = [Double]()
        for _ in 1...repeating{
            let start = DispatchTime.now()
            function()
            let end = DispatchTime.now()
            let nanoseconds = Double(end.uptimeNanoseconds -start.uptimeNanoseconds)
            let milliseconds = nanoseconds / 1e6
            times.append(milliseconds)
        }
        print("\(times.reduce(0.0, +)/Double(times.count)) ms")
    }
    
  4. In the notebookToScript function, I would combine the multiple “if” statements inside the “for” loop into 1 “if” statement (shown below).

    for cell in cells{
         if let source = cell["source"] as? [String], !source.isEmpty,
             source[0].range(of: #"^\s*//\s*export\s*$"#, options: .regularExpression) != nil{
             module.append("\n" + source[1...].joined() + "\n")
         }
     }
    
  5. In the exportNotebooks function I would combine the for loop and if statements using a “where” clause (shown below).

    public func exportNotebooks(_ path: Path){
        for entry in try! path.ls() where entry.kind == Entry.Kind.file && 
            entry.path.basename().range(of: #"^\d*_.*ipynb$"#, options: .regularExpression) != nil {
            print("Converting \(entry.path.basename())")
            notebookToScript(fname: entry.path.basename())
        }
    }
2 Likes

Thanks so much!

@stephenjohnson I’ve pushed those changes and some other little cleanups. Many thanks - it was nice to learn about where and multiple if conditionals.

1 Like

You’re welcome. Yes, Swift has some cool ways of doing things. I started looking at a couple more notebooks and I couldn’t get the 01a_fastai_layers.ipynb ones to compile without making some changes. I’m not sure if it works for you and I’m doing something wrong or not. I noticed you had a TODO comment about the one extension not working which I believe my changes fix. Anyway, I’ll mention my changes here in case they help in any way. Most likely I messed something up when I set up my Jupyter notebooks.

  1. You can uncomment out the extension you have commented out with the TODO, but you need to remove the Context parameters. You’re definition for the forward function and the didProduceActivation functions don’t have those parameters. Or conversely, you’d need to add those parameters to those functions and then leave them in this one. I just took them out of this one since none of the calls in any of the other structs had them.

    extension FALayer {
        @differentiable
        public func applied(to input: Input) -> Output {
            let activation = forward(input)
            delegate.didProduceActivation(activation)
            return activation
        }
    }
    
  2. I needed to add the typealias definitions to each struct. Not sure how it works for you without them. Again I may be doing something wrong.

    @_fixed_layout
    public struct FADense<Scalar: TensorFlowFloatingPoint>: FALayer {  
        public typealias Output = Tensor<Scalar>
        public typealias Input = Tensor<Scalar>
    
    @_fixed_layout
    public struct FANoBiasConv2D<Scalar: TensorFlowFloatingPoint>: FALayer {
        public typealias Output = Tensor<Scalar>
        public typealias Input = Tensor<Scalar>
    
    @_fixed_layout
    public struct FAConv2D<Scalar: TensorFlowFloatingPoint>: FALayer {
        public typealias Output = Tensor<Scalar>
        public typealias Input = Tensor<Scalar>
    
    @_fixed_layout
    public struct FAAvgPool2D<Scalar: TensorFlowFloatingPoint>: FALayer {
        public typealias Output = Tensor<Scalar>
        public typealias Input = Tensor<Scalar>
    
    @_fixed_layout
    public struct FAAdaptiveAvgPool2D<Scalar: TensorFlowFloatingPoint>: FALayer {
        public typealias Output = Tensor<Scalar>
        public typealias Input = Tensor<Scalar>

Thanks, I’m slowly working through these, and I’m hacking on #1 at the moment. I appreciate your changes!

1 Like

@stephenjohnson what issues were you encountering with the fastai layers notebook? (Note: the last 48 hours have been a little tricky to get a compatible tool chain + notebook configuration, but they should be set up for the latest toolchain builds. That said, they won’t work for toolchains from this morning.)

All the best,
-Brennan

I was going through the updated 00_load_data and thought that students new to Swift might be interested / amused in the flexibility of extending standard types, which is very much akin to some of Python’s dynamic features. So how about introducing a silly convenience extension that would allow you to use something like "/bin/ls -lh".shell()? It can be achieved like this:

extension String {
    func shell() {
        let pieces = self.split(separator: " ")
        guard let command = pieces.first else { return }
        let arguments = pieces[1...].map { String($0) }
        if let result = shellCommand(String(command), arguments) {
            print(result)
        }
    }
}

"/bin/ls -lh".shell()
2 Likes

The 01a_fastai_layers notebook works for me after installing the latest nightly toolchain currently published.

They weren’t compiling. There were errors in regards to Context not being defined or something like that hence the typealias declarations I added. My tool chain may have not been the correct version.

There was a change in the initializers of Context. Probably you need to update your tool chain.

Thank you. Will do.

Is there a shift+tab or {name}?? equivalent for swift-jupyter to look inside the documentation of a declaration ? something like Xcode’s option+click.

Not aware of that but Google Colab has some nifty features on their way such as autocomplete and a better dark mode. To enable the experimental mode, look for that icon in the top right corner next to your user account image:

image

image

1 Like