Skip to content

Commit

Permalink
Add functionality to Visual Recognition to update a classifier (watso…
Browse files Browse the repository at this point in the history
…n-developer-cloud#399)

* Adding code for updating a classifier.

* Adding tests for updateClassifier

* Update formatting for updateClassifier function

* Add faster test for updateClassifier
  • Loading branch information
sccheng authored and glennrfisher committed Aug 9, 2016
1 parent e243cc8 commit d3cf3ea
Show file tree
Hide file tree
Showing 2 changed files with 310 additions and 0 deletions.
225 changes: 225 additions & 0 deletions Source/VisualRecognitionV3/Tests/VisualRecognitionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,231 @@ class VisualRecognitionTests: XCTestCase {
waitForExpectations()
}

/** Update the trained classifier. */
func testUpdateClassifier() {
let description = "Update the trained classifier."
let expectation = expectationWithDescription(description)

let car = Class(name: "car", examples: examplesCars)
visualRecognition.updateClassifier(classifierID!, positiveExamples: [car], negativeExamples: examplesTrucks, failure: failWithError) {
classifier in
XCTAssertEqual(classifier.name, self.classifierName)
expectation.fulfill()
}
waitForExpectations()
}

/** Update the classifier with a positive example. */
func testUpdateClassifierWithPositiveExample() {
let description1 = "Train a new classifier with positive examples."
let expectation1 = expectationWithDescription(description1)

let name = "swift-sdk-unit-test-positive-update"
let cars = Class(name: "car", examples: examplesCars)
let trucks = Class(name: "truck", examples: examplesTrucks)

var classifierID: String?
visualRecognition.createClassifier(
name,
positiveExamples: [cars],
negativeExamples: examplesBaseball,
failure: failWithError) { classifier in
XCTAssertEqual(classifier.name, name)
XCTAssertEqual(classifier.classes.count, 1)
classifierID = classifier.classifierID
expectation1.fulfill()
}
waitForExpectations()

var trained = false
var tries = 0
while(!trained) {
tries += 1
let description = "Get the new classifier."
let expectation = expectationWithDescription(description)
visualRecognition.getClassifier(classifierID!, failure: failWithError) {
classifier in

if classifier.status == "ready" {
trained = true
}
expectation.fulfill()
}
waitForExpectations()

if tries > 5 {
let description = "Delete the new classifier."
let expectation = expectationWithDescription(description)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation.fulfill()
}
waitForExpectations()

XCTFail("Could not train a new classifier. Try again later.")
}

sleep(5)
}

let description2 = "Update the classifier with a positive example."
let expectation2 = expectationWithDescription(description2)

visualRecognition.updateClassifier(
classifierID!,
positiveExamples: [trucks],
failure: failWithError) { classifier in
XCTAssertEqual(classifier.name, name)
expectation2.fulfill()
}
waitForExpectations()

trained = false
tries = 0
while(!trained) {
tries += 1
let description = "Get the updated classifier and make sure there are 2 classes."
let expectation = expectationWithDescription(description)
visualRecognition.getClassifier(classifierID!, failure: failWithError) {
classifier in

if classifier.status == "ready" {
XCTAssertEqual(classifier.classes.count, 2)
trained = true
}
expectation.fulfill()
}
waitForExpectations()

if tries > 5 {
let description = "Delete the new classifier."
let expectation = expectationWithDescription(description)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation.fulfill()
}
waitForExpectations()

XCTFail("Could not update the classifier. Try again later.")
}

sleep(5)
}

let description4 = "Delete the custom classifier."
let expectation4 = expectationWithDescription(description4)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation4.fulfill()
}
waitForExpectations()
}

/** Update the classifier with a negative example. */
func testUpdateClassifierWithNegativeExample() {
let description1 = "Train a new classifier with positive examples."
let expectation1 = expectationWithDescription(description1)

let name = "swift-sdk-unit-test-negative-update"
let cars = Class(name: "car", examples: examplesCars)
let classes = [cars]

var classifierID: String?
visualRecognition.createClassifier(
name,
positiveExamples: classes,
negativeExamples: examplesTrucks,
failure: failWithError) { classifier in
XCTAssertEqual(classifier.name, name)
XCTAssertEqual(classifier.classes.count, 1)
classifierID = classifier.classifierID
expectation1.fulfill()
}
waitForExpectations()

var trained = false
var tries = 0
while(!trained) {
tries += 1
let description = "Get the new classifier."
let expectation = expectationWithDescription(description)
visualRecognition.getClassifier(classifierID!, failure: failWithError) {
classifier in

if classifier.status == "ready" {
trained = true
}
expectation.fulfill()
}
waitForExpectations()

if tries > 5 {
let description = "Delete the new classifier."
let expectation = expectationWithDescription(description)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation.fulfill()
}
waitForExpectations()

XCTFail("Could not train a new classifier. Try again later.")
}

sleep(5)
}

let description2 = "Update the classifier with a negative example."
let expectation2 = expectationWithDescription(description2)
visualRecognition.updateClassifier(
classifierID!,
negativeExamples: examplesBaseball,
failure: failWithError) { classifier in
XCTAssertEqual(classifier.name, name)
expectation2.fulfill()
}
waitForExpectations()

trained = false
tries = 0
while(!trained) {
tries += 1
let description = "Get the updated classifier and make sure there is 1 class."
let expectation = expectationWithDescription(description)
visualRecognition.getClassifier(classifierID!, failure: failWithError) {
classifier in

if classifier.status == "ready" {
XCTAssertEqual(classifier.classes.count, 1)
trained = true
}
expectation.fulfill()
}
waitForExpectations()

if tries > 5 {
let description = "Delete the new classifier."
let expectation = expectationWithDescription(description)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation.fulfill()
}
waitForExpectations()

XCTFail("Could not update the classifier. Try again later.")
}

sleep(5)
}

let description4 = "Delete the custom classifier."
let expectation4 = expectationWithDescription(description4)

visualRecognition.deleteClassifier(classifierID!, failure: failWithError) {
expectation4.fulfill()
}
waitForExpectations()
}

/** Classify an image by URL using the default classifier and all default parameters. */
func testClassifyByURL1() {
let description = "Classify an image by URL using the default classifier."
Expand Down
85 changes: 85 additions & 0 deletions Source/VisualRecognitionV3/VisualRecognition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,91 @@ public class VisualRecognition {
}
}

/**
Update an existing classifier by adding new classes or by adding new images to existing
classes. At least one compressed file must be passed in.

- parameter classifierID: The ID of the classifier you want to update.
- parameter positiveExamples: An array of classes, each with a name and a zip archive file of
images that prominently depict the visual subject of the class. Each class requires a
minimum of 10 images. If you specify multiple classes, the system will learn to classify
each category.
- parameter negativeExamples: A zip archive file of images that *do not* prominently depict the
visual subject of *any* of the classes being trained. Must contain a minimum of 10
images.
- parameter failure: A function executed if an error occurs.
- parameter success: A function executed with information about the updated classifier.
*/
public func updateClassifier(
classifierID: String,
positiveExamples: [Class]? = nil,
negativeExamples: NSURL? = nil,
failure: (NSError -> Void)? = nil,
success: Classifier -> Void)
{
// ensure there is at least one compressed file
guard (positiveExamples != nil) || (negativeExamples != nil) else {
let failureReason = "To update a classifier, you must provide at least one " +
"compressed file of either positive or negative examples."
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
let error = NSError(domain: domain, code: 0, userInfo: userInfo)
failure?(error)
return
}

// construct query parameters
var queryParameters = [NSURLQueryItem]()
queryParameters.append(NSURLQueryItem(name: "api_key", value: apiKey))
queryParameters.append(NSURLQueryItem(name: "version", value: version))

// construct REST request
let request = RestRequest(
method: .POST,
url: serviceURL + "/v3/classifiers/\(classifierID)",
acceptType: "application/json",
userAgent: userAgent,
queryParameters: queryParameters
)

// execute REST request
Alamofire.upload(request,
multipartFormData: { multipartFormData in
if let positiveExamples = positiveExamples {
for positiveExample in positiveExamples {
let name = positiveExample.name + "_positive_examples"
if let examples = positiveExample.examples {
multipartFormData.appendBodyPart(fileURL: examples, name: name)
}
}
}
if let negativeExamples = negativeExamples {
let examples = negativeExamples
let name = "negative_examples"
multipartFormData.appendBodyPart(fileURL: examples, name: name)
}
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseObject(dataToError: self.dataToError) {
(response: Response<Classifier, NSError>) in
switch response.result {
case .Success(let classifier): success(classifier)
case .Failure(let error): failure?(error)
}
}
case .Failure:
let failureReason = "Provided file(s) could not be encoded as form data."
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
let error = NSError(domain: self.domain, code: 0, userInfo: userInfo)
failure?(error)
return
}
}
)

}

/**
Classify images by URL. The supported image formats include .jpg, .png, and .gif.

Expand Down

0 comments on commit d3cf3ea

Please sign in to comment.