Scope
I'm trying to transfer learn a SSD MobileNet v3 (small) model using the object detection API, but my dataset has only 8 classes, while the provided model is pre-trained on COCO (90 classes). If I leave the number of classes of the model intact, I can train with no problem.
Problem
Changing the pipeline.config num_classes yields an assignment error because the layers shape doesn't match with checkpoint variables:
tensorflow.python.framework.errors_impl.InvalidArgumentError: 2 root error(s) found.
(0) Invalid argument: Assign requires shapes of both tensors to match. lhs shape= [1,1,288,27] rhs shape= [1,1,288,273]
[[{{node save/Assign_15}}]]
(1) Invalid argument: Assign requires shapes of both tensors to match. lhs shape= [1,1,288,27] rhs shape= [1,1,288,273]
[[{{node save/Assign_15}}]]
[[save/RestoreV2/_404]]
Questions
Is there any way to change the number of classes and still do transfer learning (like loading only the variables with matching size)? Or do I have to cope between training from scratch with only 8 classes or fine tuning with 90 classes?
Is there any tool to manually "trim" the pre trained checkpoint variables?
Dataset: ITD Dataset
Model: SSD MobileNetV3 - small (from the Model Zoo)
pipeline.config:
# SSDLite with Mobilenet v3 small feature extractor.
# Trained on COCO14, initialized from scratch.
# TPU-compatible.
# Users should configure the fine_tune_checkpoint field in the train config as
# well as the label_map_path and input_path fields in the train_input_reader and
# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that
# should be configured.
model {
ssd {
inplace_batchnorm_update: true
freeze_batchnorm: false
num_classes: 8
box_coder {
faster_rcnn_box_coder {
y_scale: 10.0
x_scale: 10.0
height_scale: 5.0
width_scale: 5.0
}
}
matcher {
argmax_matcher {
matched_threshold: 0.5
unmatched_threshold: 0.5
ignore_thresholds: false
negatives_lower_than_unmatched: true
force_match_for_each_row: true
use_matmul_gather: true
}
}
similarity_calculator {
iou_similarity {
}
}
encode_background_as_zeros: true
anchor_generator {
ssd_anchor_generator {
num_layers: 6
min_scale: 0.2
max_scale: 0.95
aspect_ratios: 1.0
aspect_ratios: 2.0
aspect_ratios: 0.5
aspect_ratios: 3.0
aspect_ratios: 0.3333
}
}
image_resizer {
fixed_shape_resizer {
height: 320
width: 320
}
}
box_predictor {
convolutional_box_predictor {
min_depth: 0
max_depth: 0
num_layers_before_predictor: 0
use_dropout: false
dropout_keep_probability: 0.8
kernel_size: 3
use_depthwise: true
box_code_size: 4
apply_sigmoid_to_scores: false
class_prediction_bias_init: -4.6
conv_hyperparams {
activation: RELU_6,
regularizer {
l2_regularizer {
weight: 0.00004
}
}
initializer {
random_normal_initializer {
stddev: 0.03
mean: 0.0
}
}
batch_norm {
train: true,
scale: true,
center: true,
decay: 0.97,
epsilon: 0.001,
}
}
}
}
feature_extractor {
type: 'ssd_mobilenet_v3_small'
min_depth: 16
depth_multiplier: 1.0
use_depthwise: true
conv_hyperparams {
activation: RELU_6,
regularizer {
l2_regularizer {
weight: 0.00004
}
}
initializer {
truncated_normal_initializer {
stddev: 0.03
mean: 0.0
}
}
batch_norm {
train: true,
scale: true,
center: true,
decay: 0.97,
epsilon: 0.001,
}
}
override_base_feature_extractor_hyperparams: true
}
loss {
classification_loss {
weighted_sigmoid_focal {
alpha: 0.75,
gamma: 2.0
}
}
localization_loss {
weighted_smooth_l1 {
delta: 1.0
}
}
classification_weight: 1.0
localization_weight: 1.0
}
normalize_loss_by_num_matches: true
normalize_loc_loss_by_codesize: true
post_processing {
batch_non_max_suppression {
score_threshold: 1e-8
iou_threshold: 0.6
max_detections_per_class: 100
max_total_detections: 100
use_static_shapes: true
}
score_converter: SIGMOID
}
}
}
train_config: {
batch_size: 16 #512
sync_replicas: true
startup_delay_steps: 0
replicas_to_aggregate: 32
num_steps: 0
data_augmentation_options {
ssd_random_crop_pad_fixed_aspect_ratio {
}
}
data_augmentation_options {
random_horizontal_flip {
}
}
optimizer {
momentum_optimizer: {
learning_rate: {
cosine_decay_learning_rate {
learning_rate_base: 0.4
total_steps: 800000
warmup_learning_rate: 0.13333
warmup_steps: 2000
}
}
momentum_optimizer_value: 0.9
}
use_moving_average: false
}
fine_tune_checkpoint: "./model/model.ckpt"
fine_tune_checkpoint_type: "detection"
fine_tune_checkpoint_version: V1
keep_checkpoint_every_n_hours: 2.0
max_number_of_boxes: 100
unpad_groundtruth_tensors: false
}
train_input_reader: {
tf_record_input_reader {
input_path: "./data/train.record"
}
label_map_path: "./annotations/label_map.pbtxt"
shuffle: true
}
eval_config: {
num_examples: 1296
}
eval_input_reader: {
tf_record_input_reader {
input_path: "./data/val.record"
}
label_map_path: "./annotations/label_map.pbtxt"
shuffle: true
num_readers: 1
}
Yes it is mainly the idea of the Tensorflow object detection garden to fine-tune the models! You should change :
fine_tune_checkpoint_type = "detection"
to :
fine_tune_checkpoint_type = "fine_tune"
and then when you call the object_detection/model_main*.py, you should be careful that the model_dir you are passing as argument is empty. In that way the script will be able to load the fine_tune_checkpoint you pointed at in the config with the 90 classes, and it will create a new checkpoint in your empty model directory with the saved weights and your 8 classes. After that you will even be able to load previous your previous custom checkpoint in case your training as stopped.
EDIT : reference for the fine-tune input check this answer : https://github.com/tensorflow/models/issues/8892#issuecomment-680207038
Related
My model doesn´t learn.. It is supposed to do a softmax calculation in the end. I want as a result a classification (quit or no-quit). The model should predict if the customer will quit. I am giving the quit-column as label and have 196 input-features.
My visor says there is no learning at all. But then I am not certain, how the visor will get information, if my model learns. I am very new to javascript and would appreciate any help.
ngOnInit() {
this.train();
}
async train(): Promise<any> {
const csvUrl = '/assets/little.csv';
const csvDataset = tf.data.csv(
csvUrl,
{
columnConfigs: {
quit: {
isLabel: true
}
},
delimiter:','
});
const numOfFeatures = (await csvDataset.columnNames()).length -1;
console.log(numOfFeatures);
const flattenedDataset =
csvDataset
.map(({xs, ys}: any) =>
{
// Convert xs(features) and ys(labels) from object form (keyed by
// column name) to array form.
return {xs:Object.values(xs), ys:Object.values(ys)};
}).batch(10);
console.log(flattenedDataset.toArray());
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [196], units: 100, activation: 'relu'}),
tf.layers.dense({units: 100, activation: 'relu'}),
tf.layers.dense({units: 100, activation: 'relu'}),
tf.layers.dense({units: 1, activation: 'softmax'}),
]
});
await trainModel(model, flattenedDataset);
const surface = { name: 'Model Summary', tab: 'Model Inspection'};
tfvis.show.modelSummary(surface, model);
console.log('Done Training');
}
async function trainModel(model, flattenedDataset) {
// Prepare the model for training.
model.compile({
optimizer: tf.train.adam(),
loss: tf.losses.sigmoidCrossEntropy,
metrics: ['accuracy']
});
const batchSize = 32;
const epochs = 50;
return await model.fitDataset(flattenedDataset, {
batchSize,
epochs,
shuffle: true,
callbacks: tfvis.show.fitCallbacks(
{ name: 'Training Performance' },
['loss'],
{ height: 200, callbacks: ['onEpochEnd'] }
)
});
}
The number of units for the last layer is the number of categories. There are two categories quit and no-quit. Additionnaly your labels should be one-hot encoded. More general answers on why a model is not learning can be found here.
I'm evaluating Highcharts for a project and it seems to be missing a built-in feature I would need.
For a spline, I need to assign one colour if the slope is positive, and another if it is negative, like this:
Before I invest the time in learning the API, it would be helpful to know if this is doable. A working example would be much appreciated.
You can use dynamically calculated zones, for example:
events: {
load: function() {
let points = this.series[0].points,
zones = [],
color;
function addZone(value, color) {
zones.push({
value: value,
color: color
});
}
points.forEach(function(p, i) {
if (points[i - 1]) {
if (p.y >= points[i - 1].y) {
if (color && color !== 'green') {
addZone(points[i - 1].x, 'red');
}
color = 'green';
} else if (p.y <= points[i - 1].y) {
if (color && color !== 'red') {
addZone(points[i - 1].x, 'green');
}
color = 'red';
}
}
addZone(p.x, color);
});
this.series[0].update({
zones: zones
});
}
}
Live demo: http://jsfiddle.net/BlackLabel/jmckag4q/1/
API Reference: https://api.highcharts.com/highcharts/series.line.zones
To add delays in my tests I implemented this:
func execute(after: TimeInterval, testBlock: () -> Void) {
let result = XCTWaiter.wait(for: [expectation(description: "Delayed Test")], timeout: after)
if result == XCTWaiter.Result.timedOut {
testBlock()
} else {
XCTFail("Delay interrupted.")
}
}
Then I wrote a test:
func testExecute() {
var i = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.40) {
i = 2
}
execute(after: 0.20) {
XCTAssert(i == 1)
}
execute(after: 0.15) {
XCTAssert(i == 1) // Fails once every three or four runs.
}
execute(after: 0.06) { // Never fails.
XCTAssert(i == 2)
}
}
Why does this second XCTAssert() fail regularly?
This is the only thing running on my simulator. You'd expect some jitter, but shouldn't that stay with 1 or 2 times the system clock period of 1/60s?
It turns out that delays may take up to considerably longer (up to 200ms in this 2011 experiment: http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/).
Must take sufficient margins when using this execute(after:testBlock:) function.
I'm using Appcelerator to provide a geo track. This works very well on Android, but on the iOS platform I am not getting speed or heading information from the onLocation event.
I initialise my GPS:
permissions.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_ALWAYS, function (e) {
if (e.success && !configuredMonitoring) {
if (OS_IOS) {
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST;
Ti.Geolocation.distanceFilter = 5;
Ti.Geolocation.preferredProvider = Ti.Geolocation.PROVIDER_GPS;
Ti.Geolocation.pauseLocationUpdateAutomatically = true;
Ti.Geolocation.activityType = Ti.Geolocation.ACTIVITYTYPE_OTHER_NAVIGATION;
}
if (OS_ANDROID) {
Ti.Geolocation.Android.addLocationProvider(Ti.Geolocation.Android.createLocationProvider({
name: Ti.Geolocation.PROVIDER_GPS,
minUpdateDistance: 10,
minUpdateTime: 1
}));
Ti.Geolocation.Android.addLocationRule(Ti.Geolocation.Android.createLocationRule({
provider: Ti.Geolocation.PROVIDER_GPS,
accuracy: 10,
maxAge: 5000,
minAge: 1000
}));
Ti.Geolocation.Android.addLocationProvider(Ti.Geolocation.Android.createLocationProvider({
name: Ti.Geolocation.PROVIDER_NETWORK,
minUpdateDistance: 10,
minUpdateTime: 1
}));
Ti.Geolocation.Android.addLocationRule(Ti.Geolocation.Android.createLocationRule({
provider: Ti.Geolocation.PROVIDER_NETWORK,
accuracy: 10,
maxAge: 5000,
minAge: 1000
}));
Ti.Geolocation.Android.manualMode = true;
}
}
Then I add eventlisteners for iOS location updates being paused
if (Ti.Geolocation.locationServicesEnabled){
Ti.Geolocation.addEventListener('location', onLocation);
if (OS_IOS) {
Ti.Geolocation.addEventListener('locationupdatepaused', onLocationupdate);
Ti.Geolocation.addEventListener('locationupdateresumed', onLocationupdate);
}
}
My onLocation function runs
function onLocation(e) {
if (!e.error) {
var coords = e.coords;
console.log(JSON.stringify(e));
} else {
console.log('Error!':+JSON.stringify(e))
}
}
in my return data I see
{
"success":true,
"coords:{
"timestamp":1488757841662,
"speed":-1,
"longitude":173.2793426513672,
"floor":{"level":0},
"latitude":-41.274879455566406,
"accuracy":65,
"heading":-1,
"altitude":3.9126133918762207,
"altitudeAccuracy":10
},
"code":0,
"bubbles":true,
"type":"location",
"source":{
"preferredProvider":null
},
"cancelBubble":false
}
I never see anything other than -1 in my speed or heading, which according to the documentation: "On iOS, a negative value indicates that the heading data is not valid."
What am I doing wrong here?
We had this same issue and were pulling our hair out trying to figure it out. Try changing Ti.Geolocation.ACCURACY_BEST to Ti.Geolocation.ACCURACY_BEST_FOR_NAVIGATION. This fixed it for us.
Hello i am trying to do different styles for different features and i got and working fine,now i want to set different resolution for different features.I tried doing this way but not working.Can u please Help this?
style: function (feature, resolution) {
//var test = (resolution >= 200) ? (feature.get('class') === 'xx') : '';
//resolution: test;
if (feature.get('class') === '---') {
max Resolution: 100;
return style1;
}
else if (feature.get('class') === 'xx')
{
return Style2;
}
else if (feature.get('class') === '---')
{
return style3;
}
else if(feature.get('class')==='ii')
{
return style4;
}
else if(feature.get('class')==='mm')
{
return style5;
}
},
You should be able to resolve this with simple boolean logic. Something like this:
style: function(feature, resolution) {
var class = feature.get('class');
if (resolution >= 200) {
if (class == 'xx') {
return style200xx;
} else if (class == 'xy') {
return style200xy;
}
} else if (resolution < 200) {
if (class == 'xx') {
return style0xx;
} else if (class == 'xy') {
return style0xy;
}
}
}
If you have many different cases, it might make sense to define a separate ol.layer.Vector for each resolution range, and use the same source for all layers. Inside the style functions, you will then only have to handle the feature class.