CNN_space / predict.py
shiko217's picture
fix
b56c956 unverified
Raw
History Blame Contribute Delete
5.88 kB
import os
import argparse
import numpy as np
import librosa
import scipy.signal
from scipy.stats import kurtosis
import tensorflow as tf
import warnings
warnings.filterwarnings('ignore', category=UserWarning)
# Memory optimizations for TensorFlow
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
# Constants
MAX_TIME_FRAMES = 313
N_MELS = 128
N_1D_FEATURES = 22
LABEL_MAP_INVERSE = {
0: 'Machine 1_Normal', 1: 'Machine 1_Abnormal',
2: 'Machine 2_Normal', 3: 'Machine 2_Abnormal',
4: 'Machine 3_Normal', 5: 'Machine 3_Abnormal'
}
# ==========================================
# 1. PREPROCESSOR
# ==========================================
class MachineListenerPreprocessor:
def __init__(self, target_sr=16000, n_fft=2048, hop_length=512, n_mels=N_MELS, n_mfcc=20):
self.sr = target_sr
self.n_fft = n_fft
self.hop_length = hop_length
self.n_mels = n_mels
self.n_mfcc = n_mfcc
def _apply_highpass_filter(self, y, cutoff=60.0):
nyquist = 0.5 * self.sr
normal_cutoff = cutoff / nyquist
if normal_cutoff >= 1.0:
return y
b, a = scipy.signal.butter(4, normal_cutoff, btype='high', analog=False)
return scipy.signal.filtfilt(b, a, y)
def _truncate_silence(self, y, top_db=25):
y_trimmed, _ = librosa.effects.trim(y, top_db=top_db, frame_length=self.n_fft, hop_length=self.hop_length)
return y_trimmed
def _mean_variance_normalize(self, y):
return (y - np.mean(y)) / (np.std(y) + 1e-8)
def process_audio(self, file_path):
y, _ = librosa.load(file_path, sr=self.sr)
y = self._apply_highpass_filter(y)
y = self._truncate_silence(y, top_db=25)
if len(y) == 0:
raise ValueError(f"Silence only: {file_path}")
y = self._mean_variance_normalize(y)
# 2D Features
mel_spec = librosa.feature.melspectrogram(
y=y, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length, n_mels=self.n_mels
)
log_mel_spec = librosa.power_to_db(mel_spec, ref=np.max)
# 1D Features
mfccs_mean = np.mean(librosa.feature.mfcc(S=log_mel_spec, n_mfcc=self.n_mfcc), axis=1)
centroid_mean = np.mean(librosa.feature.spectral_centroid(
y=y, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length
))
stft_mag = np.abs(librosa.stft(y, n_fft=self.n_fft, hop_length=self.hop_length))
frame_kurtosis = np.nan_to_num(kurtosis(stft_mag, axis=0, fisher=True, bias=False))
kurtosis_mean = np.mean(frame_kurtosis)
return {
"2d_spectrogram": log_mel_spec,
"1d_statistics": np.hstack([mfccs_mean, centroid_mean, kurtosis_mean])
}
def pad_or_truncate(spectrogram, max_frames):
if spectrogram.shape[1] > max_frames:
return spectrogram[:, :max_frames]
elif spectrogram.shape[1] < max_frames:
pad_width = max_frames - spectrogram.shape[1]
return np.pad(spectrogram, pad_width=((0, 0), (0, pad_width)), mode='constant')
return spectrogram
# ==========================================
# 2. CUSTOM LAYER FOR MODEL LOADING
# ==========================================
@tf.keras.utils.register_keras_serializable()
class SpecAugmentLayer(tf.keras.layers.Layer):
def __init__(self, freq_mask_param=15, time_mask_param=30, **kwargs):
super().__init__(**kwargs)
self.freq_mask_param = freq_mask_param
self.time_mask_param = time_mask_param
def call(self, inputs, training=None):
return inputs # During inference, SpecAugment does nothing
def get_config(self):
config = super().get_config()
config.update({
"freq_mask_param": self.freq_mask_param,
"time_mask_param": self.time_mask_param,
})
return config
# ==========================================
# 3. INFERENCE LOGIC
# ==========================================
def main():
parser = argparse.ArgumentParser(description="Classify machine audio.")
parser.add_argument("audio_path", help="Path to the .wav audio file")
parser.add_argument("--model", default="best_v2f_generalist.keras", help="Path to model")
args = parser.parse_args()
if not os.path.exists(args.audio_path):
print(f"Error: Audio file not found at {args.audio_path}")
return
# Enable custom layers & lambda loading
try:
import keras
keras.config.enable_unsafe_deserialization()
except:
pass
print("Loading model...")
custom_objects = {'SpecAugmentLayer': SpecAugmentLayer}
model = tf.keras.models.load_model(args.model, custom_objects=custom_objects)
print("Extracting features...")
preprocessor = MachineListenerPreprocessor()
features = preprocessor.process_audio(args.audio_path)
# Prepare inputs
spec_2d = pad_or_truncate(features["2d_spectrogram"], MAX_TIME_FRAMES)
stat_1d = features["1d_statistics"]
# Add batch and channel dimensions
spec_2d_batch = np.expand_dims(spec_2d, axis=0) # (1, 128, 313)
spec_2d_batch = np.expand_dims(spec_2d_batch, axis=-1) # (1, 128, 313, 1)
stat_1d_batch = np.expand_dims(stat_1d, axis=0) # (1, 22)
print("Running prediction...")
# model(inputs, training=False) is much faster and lighter than model.predict()
predictions = model([spec_2d_batch, stat_1d_batch], training=False).numpy()
predicted_class_idx = np.argmax(predictions, axis=-1)[0]
predicted_label = LABEL_MAP_INVERSE.get(predicted_class_idx, "Unknown")
confidence = float(np.max(predictions))
print("\n" + "="*40)
print(f"File: {os.path.basename(args.audio_path)}")
print(f"Result: {predicted_label}")
print(f"Confidence: {confidence:.2%}")
print("="*40)
if __name__ == "__main__":
main()