Skip to content

Commit 487ede4

Browse files
authored
Merge pull request #484 from cpaxton/devel
Support for CoSTAR data
2 parents 30c2682 + fc5960e commit 487ede4

9 files changed

Lines changed: 373 additions & 18 deletions

File tree

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
from __future__ import print_function
2+
3+
import keras.backend as K
4+
import keras.losses as losses
5+
import keras.optimizers as optimizers
6+
import numpy as np
7+
8+
from keras.callbacks import ModelCheckpoint
9+
from keras.layers.advanced_activations import LeakyReLU
10+
from keras.layers import Input, RepeatVector, Reshape
11+
from keras.layers.embeddings import Embedding
12+
from keras.layers.merge import Concatenate, Multiply
13+
from keras.losses import binary_crossentropy
14+
from keras.models import Model, Sequential
15+
from keras.optimizers import Adam
16+
from matplotlib import pyplot as plt
17+
18+
from .robot_multi_models import *
19+
from .mhp_loss import *
20+
from .loss import *
21+
from .sampler2 import *
22+
23+
from .conditional_image import ConditionalImage
24+
from .costar import *
25+
26+
class ConditionalImageCostar(ConditionalImage):
27+
28+
def __init__(self, *args, **kwargs):
29+
super(ConditionalImageCostar, self).__init__(*args, **kwargs)
30+
self.PredictorCb = ImageWithFirstCb
31+
32+
def _makeModel(self, image, *args, **kwargs):
33+
34+
img_shape = image.shape[1:]
35+
img_size = 1.
36+
for dim in img_shape:
37+
img_size *= dim
38+
gripper_size = 1
39+
arm_size = 6
40+
41+
# =====================================================================
42+
# Load the image decoders
43+
img_in = Input(img_shape,name="predictor_img_in")
44+
img0_in = Input(img_shape,name="predictor_img0_in")
45+
#arm_in = Input((arm_size,))
46+
#gripper_in = Input((gripper_size,))
47+
#arm_gripper = Concatenate()([arm_in, gripper_in])
48+
label_in = Input((1,))
49+
ins = [img0_in, img_in]
50+
51+
encoder = MakeImageEncoder(self, img_shape)
52+
decoder = MakeImageDecoder(self, self.hidden_shape)
53+
54+
LoadEncoderWeights(self, encoder, decoder)
55+
56+
# =====================================================================
57+
# Load the arm and gripper representation
58+
h = encoder([img0_in, img_in])
59+
60+
if self.validate:
61+
self.loadValidationModels(arm_size, gripper_size, h0, h)
62+
63+
next_option_in = Input((1,), name="next_option_in")
64+
next_option_in2 = Input((1,), name="next_option_in2")
65+
ins += [next_option_in, next_option_in2]
66+
67+
# =====================================================================
68+
# Apply transforms
69+
y = Flatten()(OneHot(self.num_options)(next_option_in))
70+
y2 = Flatten()(OneHot(self.num_options)(next_option_in2))
71+
72+
tform = self._makeTransform() if not self.dense_transform else self._makeDenseTransform()
73+
tform.summary()
74+
x = tform([h,y])
75+
x2 = tform([x,y2])
76+
77+
image_out, image_out2 = decoder([x]), decoder([x2])
78+
79+
# Compute classifier on the last transform
80+
if not self.no_disc:
81+
image_discriminator = LoadGoalClassifierWeights(self,
82+
make_classifier_fn=MakeCostarImageClassifier,
83+
img_shape=img_shape)
84+
#disc_out1 = image_discriminator([img0_in, image_out])
85+
disc_out2 = image_discriminator([img0_in, image_out2])
86+
87+
# Create custom encoder loss
88+
if self.enc_loss:
89+
loss = EncoderLoss(self.image_encoder, self.loss)
90+
enc_losses = [loss, loss]
91+
enc_outs = [x, x2]
92+
enc_wts = [1e-2, 1e-2]
93+
img_loss_wt = 1.
94+
else:
95+
enc_losses = []
96+
enc_outs = []
97+
enc_wts = []
98+
img_loss_wt = 1.
99+
100+
# Create models to train
101+
if self.no_disc:
102+
disc_wt = 0.
103+
else:
104+
disc_wt = 1e-3
105+
if self.no_disc:
106+
train_predictor = Model(ins + [label_in],
107+
[image_out, image_out2] + enc_outs)
108+
train_predictor.compile(
109+
loss=[self.loss, self.loss,] + enc_losses,
110+
loss_weights=[img_loss_wt, img_loss_wt] + enc_wts,
111+
optimizer=self.getOptimizer())
112+
else:
113+
train_predictor = Model(ins + [label_in],
114+
#[image_out, image_out2, disc_out1, disc_out2] + enc_outs)
115+
[image_out, image_out2, disc_out2] + enc_outs)
116+
train_predictor.compile(
117+
loss=[self.loss, self.loss, "categorical_crossentropy"] + enc_losses,
118+
#loss_weights=[img_loss_wt, img_loss_wt, 0.9*disc_wt, disc_wt] + enc_wts,
119+
loss_weights=[img_loss_wt, img_loss_wt, disc_wt] + enc_wts,
120+
optimizer=self.getOptimizer())
121+
train_predictor.summary()
122+
123+
# Set variables
124+
self.predictor = None
125+
self.model = train_predictor
126+
127+
128+
def _getData(self, image, label, goal_idx, q, gripper, labels_to_name, *args, **kwargs):
129+
'''
130+
Parameters:
131+
-----------
132+
image: jpeg encoding of image
133+
label: integer code for which action is being performed
134+
goal_idx: index of the start of the next action
135+
q: joint states
136+
gripper: floating point gripper openness
137+
labels_to_name: list of high level actions (AKA options)
138+
'''
139+
140+
# Null option to be set as the first option
141+
# Verify this to make sure we aren't loading things with different
142+
# numbers of available options/high-level actions
143+
assert(len(labels_to_name) == self.null_option)
144+
self.null_option = len(labels_to_name)
145+
# Total number of options incl. null
146+
self.num_options = len(labels_to_name) + 1
147+
148+
length = label.shape[0]
149+
prev_label = np.zeros_like(label)
150+
prev_label[1:] = label[:(length-1)]
151+
prev_label[0] = self.null_option
152+
153+
goal_idx = np.min((goal_idx, np.ones_like(goal_idx)*(length-1)),axis=0)
154+
155+
if not (image.shape[0] == goal_idx.shape[0]):
156+
print("Image shape:", image.shape)
157+
print("Goal idxs:", goal_idx.shape)
158+
print(label)
159+
print(goal_idx)
160+
raise RuntimeError('data type shapes did not match')
161+
goal_label = label[goal_idx]
162+
goal_image = image[goal_idx]
163+
goal_image2, goal_label2 = GetNextGoal(goal_image, label)
164+
165+
# Extend image_0 to full length of sequence
166+
image0 = image[0]
167+
image0 = np.tile(np.expand_dims(image0,axis=0),[length,1,1,1])
168+
169+
lbls_1h = np.squeeze(ToOneHot2D(label, self.num_options))
170+
lbls2_1h = np.squeeze(ToOneHot2D(goal_label2, self.num_options))
171+
if self.no_disc:
172+
return ([image0, image, label, goal_label, prev_label],
173+
[goal_image,
174+
goal_image2,])
175+
else:
176+
return ([image0, image, label, goal_label, prev_label],
177+
[goal_image,
178+
goal_image2,
179+
lbls2_1h,])
180+

costar_models/python/costar_models/costar.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,40 @@
2424
for real robot execution.
2525
'''
2626

27+
def MakeCostarImageClassifier(model, img_shape, trainable=True):
28+
img0 = Input(img_shape,name="img0_classifier_in")
29+
img = Input(img_shape,name="img_classifier_in")
30+
bn = model.use_batchnorm
31+
disc = True
32+
dr = model.dropout_rate
33+
x = img
34+
x0 = img0
35+
36+
#x = AddConv2D(x, 32, [7,7], 1, 0., "same", lrelu=disc, bn=bn)
37+
x = AddConv2D(x, 32, [5,5], 2, 0., "same", lrelu=disc, bn=bn)
38+
x = Dropout(dr)(x)
39+
#x = AddConv2D(x, 32, [5,5], 1, 0., "same", lrelu=disc, bn=bn)
40+
#x = AddConv2D(x, 32, [5,5], 1, 0., "same", lrelu=disc, bn=bn)
41+
x = AddConv2D(x, 64, [5,5], 2, 0., "same", lrelu=disc, bn=bn)
42+
x = Dropout(dr)(x)
43+
#x = AddConv2D(x, 64, [5,5], 1, 0., "same", lrelu=disc, bn=bn)
44+
x = AddConv2D(x, 128, [5,5], 2, 0., "same", lrelu=disc, bn=bn)
45+
x = Dropout(dr)(x)
46+
#x = AddConv2D(x, 128, [5,5], 1, 0., "same", lrelu=disc, bn=bn)
47+
x = AddConv2D(x, 128, [5,5], 2, 0., "same", lrelu=disc, bn=bn)
48+
49+
x = Flatten()(x)
50+
#x = Dropout(0.5)(x)
51+
#x = AddDense(x, 1024, "lrelu", 0., output=True, bn=False)
52+
x = Dropout(0.5)(x)
53+
x = AddDense(x, model.num_options, "softmax", 0., output=True, bn=False)
54+
image_encoder = Model([img0, img], x, name="classifier")
55+
if not trainable:
56+
image_encoder.trainable = False
57+
image_encoder.compile(loss="categorical_crossentropy",
58+
optimizer=model.getOptimizer(),
59+
metrics=["accuracy"])
60+
model.classifier = image_encoder
61+
return image_encoder
62+
2763

costar_models/python/costar_models/depth_image_encoding.py renamed to costar_models/python/costar_models/datasets/depth_image_encoding.py

File renamed without changes.

costar_models/python/costar_models/datasets/npy_generator.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,23 @@ def load(self, success_only=False):
4848
if success_only and f.split('.')[1] == 'failure':
4949
continue
5050

51-
if i < 2:
51+
if i < 1:
5252
fsample = self._load(os.path.join(self.name, f))
5353
for key, value in fsample.items():
5454

5555
if self.load_jpeg and key in ["image", "goal_image"]:
5656
value = ConvertJpegListToNumpy(value)
5757

58-
if key not in sample:
59-
sample[key] = value
6058
if value.shape[0] == 0:
59+
sample = {}
6160
continue
62-
sample[key] = np.concatenate([sample[key],value],axis=0)
61+
62+
if key not in sample:
63+
sample[key] = value
64+
else:
65+
# Note: do not collect multiple samples anymore; this
66+
# hould never be reached
67+
sample[key] = np.concatenate([sample[key],value],axis=0)
6368
i += 1
6469
acceptable_files.append(f)
6570

costar_models/python/costar_models/discriminator.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .multi import *
1818
from .husky import *
1919
from .dvrk import *
20+
from .costar import *
2021

2122
class Discriminator(RobotMultiPredictionSampler):
2223

@@ -66,8 +67,6 @@ def __init__(self, goal, taskdef, *args, **kwargs):
6667
super(HuskyDiscriminator, self).__init__(taskdef, *args, **kwargs)
6768
self.PredictorCb = None
6869
self.goal = goal
69-
self.num_options = HuskyNumOptions()
70-
self.null_options = HuskyNullOption()
7170

7271
def _makeModel(self, image, *args, **kwargs):
7372
'''
@@ -101,7 +100,6 @@ def __init__(self, goal, taskdef, *args, **kwargs):
101100
'''
102101
super(JigsawsDiscriminator, self).__init__(taskdef, *args, **kwargs)
103102
self.PredictorCb = None
104-
self.num_options = SuturingNumOptions()
105103
self.num_generator_files = 1
106104
self.goal = goal
107105
self.load_jpeg = True
@@ -131,3 +129,43 @@ def _getData(self, image, goal_idx, label, *args, **kwargs):
131129
else:
132130
return [I0, I], [o1_1h]
133131

132+
class CostarDiscriminator(RobotMultiPredictionSampler):
133+
134+
def __init__(self, goal, taskdef, *args, **kwargs):
135+
'''
136+
As in the other models, we call super() to parse arguments from the
137+
command line and set things like our optimizer and learning rate.
138+
'''
139+
super(CostarDiscriminator, self).__init__(taskdef, *args, **kwargs)
140+
self.PredictorCb = None
141+
self.num_generator_files = 1
142+
self.goal = goal
143+
self.load_jpeg = True
144+
145+
def _makeModel(self, image, *args, **kwargs):
146+
'''
147+
Create model to predict possible manipulation goals.
148+
'''
149+
img_shape = image.shape[1:]
150+
disc = MakeCostarImageClassifier(self, img_shape)
151+
disc.summary()
152+
153+
self.model = disc
154+
155+
def _getData(self, image, goal_idx, label, *args, **kwargs):
156+
#I = np.array(image)
157+
#I_target = np.array(goal_image)
158+
I = image
159+
length = label.shape[0]
160+
goal_idx = np.min((goal_idx, np.ones_like(goal_idx)*(length-1)),axis=0)
161+
I_target = I[goal_idx]
162+
o1 = np.array(label)
163+
o1_1h = np.squeeze(ToOneHot2D(o1, self.num_options))
164+
I0 = I[0]
165+
length = I.shape[0]
166+
I0 = np.tile(np.expand_dims(I0,axis=0),[length,1,1,1])
167+
if self.goal:
168+
return [I0, I_target], [o1_1h]
169+
else:
170+
return [I0, I], [o1_1h]
171+

costar_models/python/costar_models/multi_sampler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ def _makePredictor(self, features):
293293

294294
return predictor, model, actor, ins, enc
295295

296-
def _makeTransform(self, h_dim=(8,8), perm_drop=False):
296+
def _makeTransform(self, perm_drop=False):
297297
'''
298298
This is the version made for the newer code, it is set up to use both
299299
the initial and current observed world and creates a transform
@@ -307,6 +307,7 @@ def _makeTransform(self, h_dim=(8,8), perm_drop=False):
307307
--------
308308
transform model
309309
'''
310+
h_dim = self.hidden_shape
310311
h = Input((h_dim[0], h_dim[1], self.encoder_channels),name="h_in")
311312
option = Input((self.num_options,),name="t_opt_in")
312313
# Never use the BN here?

costar_models/python/costar_models/util.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
# CoSTAR
2626
from .pretrain_image_costar import PretrainImageCostar
27+
from .conditional_image_costar import ConditionalImageCostar
28+
from .discriminator import CostarDiscriminator
2729

2830
# Jigsaws stuff
2931
from .dvrk import *
@@ -188,10 +190,26 @@ def MakeModel(features, model, taskdef, **kwargs):
188190
model=model,
189191
features=features,
190192
**kwargs)
193+
elif model == "conditional_image":
194+
model_instance = ConditionalImageCostar(taskdef,
195+
features=features,
196+
model=model,
197+
**kwargs)
198+
elif model == "discriminator":
199+
model_instance = CostarDiscriminator(False, taskdef,
200+
features=features,
201+
model=model, **kwargs)
202+
elif model == "goal_discriminator":
203+
model_instance = CostarDiscriminator(True, taskdef,
204+
features=features,
205+
model=model, **kwargs)
191206

192207
# Global setup for CoSTAR
193208
# this one uses jpegs
194209
model_instance.load_jpeg = True
210+
model_instance.null_option = 40
211+
model_instance.num_options = 41
212+
model_instance.validation_split = 0.2
195213

196214
elif features == "husky":
197215
'''

0 commit comments

Comments
 (0)