请求验证基于TensorFlow的RESNET架构二分类机器学习代码
Code Validation for TensorFlow ResNet Binary Classification Project
Hi Karl, let's walk through validating your code, focusing first on the two areas you mentioned—dataset splitting and brightness augmentation—then covering overall correctness.
1. Dataset Splitting Logic (splitdataset.ipynb)
Your core 80/10/10 split approach makes sense, but there are several critical bugs and fragilities to address:
Critical Bugs
- Mismatched Directory Names: You create directories under
data_set/but copy files tosplit_ds/. This will throw aFileNotFoundErrorunlesssplit_dsalready exists. Replace allsplit_ds/references withdata_set/to align with your directory setup. - Fragile Class Detection: Checking
image[8]to determine class relies on hardcoded path indices, which will break if your directory structure changes. Useos.pathutilities instead to safely extract the class:# Replace this: if(image[8] == "D"): im = image[14:] # With this: class_dir = os.path.basename(os.path.dirname(image)) filename = os.path.basename(image) if class_dir == "DU145": - Unsafe Filename Extraction:
image[14:]andimage[12:]are hardcoded. Useos.path.basename(image)to get the filename reliably regardless of path length.
Improvements for Robustness
- Class Balance Preservation: Combining both classes before shuffling can lead to imbalanced splits if your original dataset has unequal class sizes. Split each class separately to maintain balance:
# Split DU145 random.shuffle(du145) du145_train = du145[:int(0.8*len(du145))] du145_val = du145[int(0.8*len(du145)):int(0.9*len(du145))] du145_test = du145[int(0.9*len(du145)):] # Split PC3 similarly random.shuffle(pc3) pc3_train = pc3[:int(0.8*len(pc3))] pc3_val = pc3[int(0.8*len(pc3)):int(0.9*len(pc3))] pc3_test = pc3[int(0.9*len(pc3)):] # Combine and shuffle to mix classes train = du145_train + pc3_train val = du145_val + pc3_val test = du145_test + pc3_test random.shuffle(train) random.shuffle(val) random.shuffle(test) - Clean Existing Split Directory: Add
!rm -r data_setat the start to avoid duplicate files when re-running the script. - Handle Missing Directories: Add checks to ensure
full_ds/DU145andfull_ds/PC3exist before proceeding to avoid errors.
2. Brightness Augmentation Implementation (classifyimages.ipynb)
Your custom augmentation layer has two critical issues that will prevent it from working as intended:
Critical Bugs
- Numpy Random in Graph Mode: Using
np.random.uniforminside the layer'scallmethod will generate a single random value once when the model graph is built, not per batch or image. Replace with TensorFlow's random operations for dynamic, graph-compatible augmentation:# Replace np.random.uniform with tf.random.uniform contrast = tf.random.uniform(shape=[], minval=self.contrast_range[0], maxval=self.contrast_range[1]) brightness = tf.random.uniform(shape=[], minval=self.brightness_delta[0], maxval=self.brightness_delta[1]) - Incorrect Augmentation Order: You apply color distortion before rescaling. Since
image_dataset_from_directoryloads images as uint8 (0-255), your brightness delta ([-0.15, 0.15]) is negligible against 0-255 pixel values. Reorder your augmentation sequence to rescale first, then apply color/brightness adjustments:
This ensures your brightness/contrast adjustments operate on 0-1 normalized images, making the delta values meaningful.augment_and_normalize = tf.keras.Sequential([ tf.keras.layers.experimental.preprocessing.Rescaling(1.0 / 255), # Rescale first RandomColorDistortion(contrast_range=[0.5,1.5], brightness_delta=[-0.15, 0.15]), tf.keras.layers.RandomFlip("horizontal"), tf.keras.layers.RandomRotation(0.1), ], name="augment_and_normalize")
Minor Improvement
- Redundant Clipping:
tf.image.adjust_contrastandtf.image.adjust_brightnessalready clamp values to valid ranges for normalized images, sotf.clip_by_value(images, 0, 1)is redundant but harmless.
3. Full Code Validation Notes
Model Architecture
- Redundant Flatten Layer:
GlobalAveragePooling2Doutputs a 1D tensor, so the subsequentFlatten()layer does nothing. You can safely remove it. - Base Model Freezing: When using pre-trained ResNet50, consider freezing the base layers initially to avoid destroying pre-trained features during early training:
feature_extractor = tf.keras.applications.resnet.ResNet50(input_shape=(224, 224, 3), include_top=False, weights='imagenet') feature_extractor.trainable = False # Freeze base model # After initial training, unfreeze some layers for fine-tuning if needed - Binary Classification Output: Using
Dense(1, activation="sigmoid")withbinary_crossentropyis correct for binary classification.
Training Workflow
- ModelCheckpoint Best Weights: Your callback saves the best weights based on
val_accuracy, but you manually copyweights.05.ckpt. Instead, use a fixed filename for best weights to avoid copying arbitrary epochs:
Then copyModelCheckpoint(filepath="best_weights.ckpt", monitor='val_accuracy', save_best_only=True, save_weights_only=True)best_weights.ckpt.*to your drive. - Data Prefetching: Add prefetching to speed up training by overlapping data loading and model execution:
train_ds = train_ds.prefetch(tf.data.AUTOTUNE) val_ds = val_ds.prefetch(tf.data.AUTOTUNE) test_ds = test_ds.prefetch(tf.data.AUTOTUNE)
Corrected Code Snippets
Fixed RandomColorDistortion Layer
class RandomColorDistortion(tf.keras.layers.Layer): def __init__(self, contrast_range=[0.5, 1.5], brightness_delta=[-0.2, 0.2], **kwargs): super(RandomColorDistortion, self).__init__(**kwargs) self.contrast_range = contrast_range self.brightness_delta = brightness_delta def call(self, images, training=None): if not training: return images # Use TensorFlow random operations instead of numpy contrast = tf.random.uniform(shape=[], minval=self.contrast_range[0], maxval=self.contrast_range[1]) brightness = tf.random.uniform(shape=[], minval=self.brightness_delta[0], maxval=self.brightness_delta[1]) images = tf.image.adjust_contrast(images, contrast) images = tf.image.adjust_brightness(images, brightness) return images
Fixed Dataset Splitting Class Handling
for image in train: class_dir = os.path.basename(os.path.dirname(image)) filename = os.path.basename(image) if class_dir == "DU145": shutil.copyfile(image, "data_set/training/DU145/"+filename) elif class_dir == "PC3": shutil.copyfile(image, "data_set/training/PC3/"+filename)
These fixes will ensure your dataset splitting works reliably and your brightness augmentation operates as intended. Let me know if you need further clarification on any part!
内容的提问来源于stack exchange,提问作者karl-gardner
相关产品推荐
相关产品推荐

