Skip to content

Boston Housing example based on Pytorch instead of tensorflow/swift #4

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 42 additions & 66 deletions Examples/Regression-BostonHousing/main.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,58 @@
open Datasets
open DiffSharp
open DiffSharp.Model
open DiffSharp.Data

// Defining default hyper parameters
let batchSize = 50
let numEpochs = 200
let learningRate = 0.0001
let sizeHidden1 = 100
let sizeHidden2 = 50
let sizeHidden3 = 10
let sizeHidden4 = 1


// open Dataset
let dataset = BostonHousing()
let trainIter = DataLoader(TensorDataset(dataset.xTrain,dataset.yTrain),batchSize=batchSize, shuffle = true)

// Create Model
type RegressionModel() =
inherit Model()
let layer1 = Linear(inFeatures=13, outFeatures=64) --> dsharp.relu
let layer2 = Linear(inFeatures=64, outFeatures=32) --> dsharp.relu
let layer3 = Linear(inFeatures=32, outFeatures=1)
let layer1 = Linear(inFeatures=13, outFeatures=sizeHidden1) --> dsharp.relu
let layer2 = Linear(inFeatures=sizeHidden1, outFeatures=sizeHidden2) --> dsharp.relu
let layer3 = Linear(inFeatures=sizeHidden2, outFeatures=sizeHidden3) --> dsharp.relu
let layer4 = Linear(inFeatures=sizeHidden3,outFeatures=sizeHidden4)

do base.register()
do base.add([layer1;layer2;layer3;layer4],
["layer1";"layer2";"layer3";"layer4"])
override _.forward(input) =
input |> layer1.forward |> layer2.forward |> layer3.forward
input |> layer1.forward |> layer2.forward |> layer3.forward |> layer4.forward

let model = RegressionModel()

// Train Model
let optimizer = RMSProp(model, learningRate=dsharp.scalar 0.001)
model.mode <- Mode.Train

let epochCount = 500
let batchSize = 32
let numberOfBatch = int(ceil(Double(dataset.numTrainRecords) / double(batchSize)))
let shuffle = true

let meanAbsoluteError(predictions=Tensor, truths: Tensor) =
abs(Tensor(predictions - truths)).mean().toScalar()


print("Starting training..")

for epoch in 1..epochCount do
let epochLoss: double = 0
let epochMAE: double = 0
let batchCount: int = 0
let batchArray = Array.replicate false, count: numberOfBatch)
for batch in 0..numberOfBatch-1 do
let r = batch
if shuffle then
while true do
r = Int.random(0..numberOfBatch-1)
if not batchArray.[r] then
batchArray.[r] = true
break

let batchStart = r * batchSize
let batchEnd = min(dataset.numTrainRecords, batchStart + batchSize)
let (loss, grad) = valueWithGradient<| fun model -> = (model: RegressionModel) = Tensor in
let logits = model(dataset.xTrain[batchStart..<batchEnd])
meanSquaredError(predicted=logits, expected=dataset.yTrain[batchStart..<batchEnd])

optimizer.update(&model, along=grad)

let logits = model(dataset.xTrain[batchStart..<batchEnd])
epochMAE <- epochMAE + meanAbsoluteError(predictions=logits, truths: dataset.yTrain[batchStart..<batchEnd])
epochLoss <- epochLoss + loss.toScalar()
batchCount <- batchCount + 1

epochMAE /= double(batchCount)
epochLoss /= double(batchCount)

if epoch = epochCount-1 then
print($"MSE: {epochLoss}, MAE: {epochMAE}, Epoch: \(epoch+1)")



// Evaluate Model

print("Evaluating model..")

model.mode <- Mode.Eval

let prediction = model(dataset.xTest)

let evalMse = meanSquaredError(predicted=prediction, expected=dataset.yTest).toScalar()/double(dataset.numTestRecords)
let evalMae = meanAbsoluteError(predictions=prediction, truths: dataset.yTest)/double(dataset.numTestRecords)

print($"MSE: {evalMse}, MAE: {evalMae}")
let criterion (outputs, labels) = dsharp.mseLoss(outputs,labels)
// Train Model
let optimizer = Optim.SGD(model, learningRate= dsharp.scalar learningRate)

for epoch in 1..numEpochs do
let mutable runningLoss = 0.0
for _batch, inputs, labels in trainIter.epoch() do
model.reverseDiff()
// forward pass
let outputs = model.forward(inputs)
// defining loss
let loss = criterion(outputs,labels)
// computing gradients
loss.reverse()
// accumulating running loss
runningLoss <- runningLoss + loss.toDouble()
// updated weights based on computed gradients
optimizer.step()
if epoch % 20 = 0 then
printfn $"Epoch {epoch}/{numEpochs} running accumulative loss across all batches: %.3f{runningLoss}"

let outputs = model.forward(dataset.xTest)
let err = sqrt(dsharp.mseLoss(outputs,dataset.yTest))
42 changes: 26 additions & 16 deletions Library/Datasets/BostonHousing/BostonHousing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,50 @@ type BostonHousing() =

if not (File.Exists(downloadPath)) || Directory.GetFiles(downloadPath) |> Array.isEmpty then
DatasetUtilities.downloadResource(
filename=downloadFile,
filename=Path.GetFileName(downloadFile),
remoteRoot= remoteURL, localStorageDirectory=downloadPath,
extract=false)
|> ignore

File.ReadAllText(downloadFile, Encoding.UTF8)

// Convert Space Separated CSV with no Header
let dataRecords = data.Split("\n") |> Array.map (fun s -> s.Split(" ") |> Array.map float)
let dataRecords =
data.Split("\n")
|> Array.filter(fun line -> line <> "")
|> Array.map (fun s ->
s.Split(" ")
|> Array.filter(fun x -> x <> "")
|> Array.map float)

let numRecords = dataRecords.Length
let numColumns = dataRecords.[0].Length
let nRecords = dataRecords.Length
let nColumns = dataRecords.[0].Length

let dataFeatures = dataRecords |> Array.map (fun arr -> arr.[0..numColumns - 2])
let dataLabels = dataRecords |> Array.map (fun arr -> arr.[(numColumns - 1)..])
let dataFeatures = dataRecords |> Array.map (fun arr -> arr.[0..nColumns - 2])
let dataLabels = dataRecords |> Array.map (fun arr -> arr.[(nColumns - 1)..])

// Normalize
let trainPercentage: double = 0.8

let numTrainRecords = int(ceil(double(numRecords) * trainPercentage))
let numTestRecords = numRecords - numTrainRecords
let nTrainRecords = int(ceil(double(nRecords) * trainPercentage))
let nTestRecords = nRecords - nTrainRecords

let xTrainPrelim = dataFeatures.[0..numTrainRecords-1] |> Array.concat
let xTestPrelim = dataFeatures.[numTrainRecords..] |> Array.concat
let yTrainPrelim = dataLabels.[0..numTrainRecords-1] |> Array.concat
let yTestPrelim = dataLabels.[numTrainRecords..] |> Array.concat
let xTrainPrelim = dataFeatures.[0..nTrainRecords-1] |> Array.concat
let xTestPrelim = dataFeatures.[nTrainRecords..] |> Array.concat
let yTrainPrelim = dataLabels.[0..nTrainRecords-1] |> Array.concat
let yTestPrelim = dataLabels.[nTrainRecords..] |> Array.concat

let xTrainDeNorm = dsharp.tensor(xTrainPrelim, dtype=Dtype.Float32).view([numTrainRecords; numColumns - 1])
let xTestDeNorm = dsharp.tensor(xTestPrelim, dtype=Dtype.Float32).view([numTestRecords; numColumns - 1])
let xTrainDeNorm = dsharp.tensor(xTrainPrelim, dtype=Dtype.Float32).view([nTrainRecords; nColumns - 1])
let xTestDeNorm = dsharp.tensor(xTestPrelim, dtype=Dtype.Float32).view([nTestRecords; nColumns - 1])

let mean = xTrainDeNorm.mean(dim=0)
let std = xTrainDeNorm.stddev(dim=0)

member val numRecords = nRecords
member val numColumns = nColumns
member val numTrainRecords = nTrainRecords
member val numTestRecords = nTestRecords
member val xTrain = (xTrainDeNorm - mean) / std
member val xTest = (xTestDeNorm - mean) / std
member val yTrain = dsharp.tensor(yTrainPrelim, dtype=Dtype.Float32).view([numTrainRecords; 1])
member val yTest = dsharp.tensor(yTestPrelim, dtype=Dtype.Float32).view([numTestRecords; 1])
member val yTrain = dsharp.tensor(yTrainPrelim, dtype=Dtype.Float32).view([nTrainRecords; 1])
member val yTest = dsharp.tensor(yTestPrelim, dtype=Dtype.Float32).view([nTestRecords; 1])
2 changes: 1 addition & 1 deletion Library/Datasets/DatasetUtilities.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type DatasetUtilities() =
let extract = defaultArg extract true
if not extract then
use wc = new WebClient()
wc.DownloadFile(remoteRoot, localFileName)
wc.DownloadFile(Uri(remoteRoot,filename), localFileName)
else
failwith "TBD" // let r = new BinaryReader(new GZipStream(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress))
localFileName
Expand Down