Sunil Tyagi, PhD

Working note · Deep learning

Re-identifying vessels from radiated noise

Embeddings, galleries and enrolling new ships without retraining: how an acoustic re-identification system decides which vessel it is hearing, or that it has never heard it, with a runnable example in NumPy.

You add a new vessel to an acoustic re-identification system without retraining anything. You pass a few recordings of the vessel once through the trained network, which stays frozen, and add the fingerprints it returns to a gallery; every later recording is compared with that gallery. I am Sunil Tyagi, and I build such systems at Oravont Systems LLP in Noida, India. This note explains the design: what is learned once, what grows in the field, and how the system decides that a contact is unknown.

Acoustic vessel re-identification is the task of deciding, from a passive recording of a ship’s underwater radiated noise, which individual, previously recorded vessel it is, or that it is none of them. It is also called ship re-identification, acoustic fingerprinting of vessels and individual vessel identification. The recordings come from hydrophones or passive sonar, which only listen.

Title card reading Re-identifying vessels from radiated noise, with two bar charts of cosine similarity: an enrolled vessel scoring above a dashed threshold, and a never-enrolled vessel whose scores all fall below it The title image shows the worked example's own output: query 1, a new passage of an enrolled vessel (top), and query 2, a vessel that was never enrolled (bottom), against the threshold of 0.50 used in the synthetic example.

Classification is not re-identification

Most machine learning on ship noise is classification by vessel type: cargo ship, tanker, tug and so on. The public datasets are built that way. DeepShip holds about 47 hours of recordings of 265 ships, labelled in four classes (Irfan et al. 2021); ShipsEar holds 90 recordings of 11 vessel types (Santos-Domínguez et al. 2016). A classifier can only answer with one of the classes it was trained on, and adding a class means retraining it.

Re-identification asks a different question: which individual hull is this? Two cargo ships of the same type must come out as two different vessels. The answer may also be “none of the ones I know”, which makes it an open-set problem: the system must be able to reject a recording rather than force it into the nearest known class (Scheirer et al. 2013).

The design that makes this possible comes from face and person recognition. The network maps each recording to an embedding, a short list of numbers that acts as a fingerprint, and identification becomes a search. The embedding of the recording being identified, the query (also called the probe), is compared with the embeddings in a gallery of known recordings, and the gallery is ranked by similarity (Schroff et al. 2015; Ye et al. 2022).

Embeddings from the raw waveform

In SKANN (Selective-Kernel Acoustic Neural Network), the encoder I developed, the input is the raw hydrophone waveform cut into 5-second segments, and the output for each segment is an embedding of 512 numbers. Each embedding is scaled to length 1 (L2-normalised), so that it lies on a sphere and only its direction matters (Tyagi 2026; Deng et al. 2019).

There is no fixed spectrogram front end. Four banks of learned filters, from about 16 ms to about 1 s long, read the waveform in parallel, so that slow machinery tonals and millisecond transients are each resolved by a filter suited to them, and a selective-kernel attention stage decides which scales to trust for each segment. A recording’s fingerprint, its passage embedding, is the mean of its segment embeddings scaled back to length 1; a passage is one recording, or encounter, of a vessel.

The encoder is trained once, with an additive angular-margin loss (ArcFace) over several hundred training vessels. The classification layer used in training is then discarded, and only the part that produces embeddings is kept (Tyagi 2026; Deng et al. 2019). The method is set out in my preprint, arXiv:2609.07399, and summarised on the SKANN page at Oravont.

Enrolment: the model stays frozen, the gallery grows

Enrolment (enrollment, in American spelling) is how a vessel becomes known. Its recordings are passed once through the frozen encoder, and each passage embedding is added to the gallery under the vessel’s name. Nothing in the network changes. A classification head could not do this, because its list of classes is fixed when it is trained (Tyagi 2026).

The gallery keeps every enrolled passage separately rather than averaging a vessel into one entry. A query is compared with every passage, and each vessel counts at its best-matching passage (Tyagi 2026). That is why several passages per vessel matter: recordings made in different conditions give a query more chances to meet one that resembles it. In the application I built, enrolling more than one passage per vessel is the single biggest lever on recall, the share of queries for which the right vessel is found.

The enrolment-and-inference application I built applies this design: the model is frozen, enrolment only grows the gallery, and each enrolled recording is stored with its fingerprint.

Flow diagram in two rows. Top row, enrolment: a known vessel's recordings, the frozen encoder, the passage fingerprint, and the gallery. Bottom row, identification: a query recording, the same encoder, the query fingerprint, cosine similarity against every enrolled passage, and a decision of candidate or unknown. An arrow from the gallery feeds the cosine-similarity step Figure 1 — Enrolment and identification. Top: a known vessel's recordings pass once through the frozen encoder; each passage fingerprint, the mean of its segment embeddings scaled to length 1, is added to the gallery. Bottom: a query recording passes through the same encoder with the same fixed settings; its fingerprint is compared by cosine similarity with every enrolled passage, each vessel keeps its best score, and the best is accepted if it reaches the threshold; otherwise the answer is unknown.

Enrolment has a limit worth stating. It adds vessels; it does not adapt the model to a new recording network. In my preprint, a fingerprint learned on one hydrophone network did not recognise vessels on another (Tyagi 2026, §IX-C).

Deciding that a contact is unknown

The similarity of two embeddings is measured by cosine similarity: the cosine of the angle between them, which for vectors of length 1 is simply their dot product. It runs from −1 to 1, and higher means more alike.

The answer is the best-matching vessel if its score reaches a threshold, and “unknown” if it does not: the decision is a thresholded similarity, not a choice from a fixed list (Tyagi 2026). Open-set recognition treats it the same way. A score must pass an operating threshold to count as a match, and the right threshold depends on the application (Scheirer et al. 2013; Ye et al. 2022).

“Unknown” here means not enrolled. A vessel the network never saw in training can still be enrolled and then recognised; a vessel that was never enrolled cannot be, however well the network was trained.

The threshold has to be set for each deployment. The operating point shifts with hardware, so the application ships with a calibration procedure and score distributions rather than one fixed threshold: the threshold is set against local ground truth on the target hardware, without retraining. The colour bands on its results (green, amber, grey) are a rough display aid, not a decision rule, and when a contact is not in the gallery, a flat list of middling scores is the correct answer. My preprint states that its own accept/reject threshold has not been calibrated against an operational false-alarm budget (Tyagi 2026).

A worked example in NumPy

The example runs the gallery logic on made-up embeddings, so that it can be read and run without a model or data. It enrols four vessels, identifies a new passage of one of them, rejects a vessel that was never enrolled, enrols that vessel and identifies it, and shows why a second and third passage help. The threshold of 0.50 is for this synthetic example only, and the scores are outputs of the synthetic model, not measurements. Download reid_gallery_example.py (Python 3 and NumPy).

reid_gallery_example.py

# reid_gallery_example.py
# Enrol vessels in a gallery, rank a query by cosine similarity and call it unknown below a threshold (NumPy, synthetic embeddings).
#
# By Sunil Tyagi (https://suniltyagi.in), for the note "Re-identifying vessels from radiated noise":
# https://suniltyagi.in/notes/vessel-re-identification/
#
# Nothing here is a trained model or real data. A real encoder maps each 5-second segment of a recording to a
# 512-number embedding; here, made-up vectors stand in for those embeddings, so that the gallery logic can be
# run and read on its own. The scores it prints come from this synthetic model, not from any measurement.
#
# Run: python reid_gallery_example.py   (Python 3 and NumPy only)

import numpy as np

DIM = 512           # embedding size
THRESHOLD = 0.50    # decision threshold for THIS synthetic example only; a real one is calibrated per deployment
rng = np.random.default_rng(7)


def unit(v):
    """L2-normalise a vector (or each row of a matrix) to length 1."""
    return v / np.linalg.norm(v, axis=-1, keepdims=True)


# --- A synthetic world: what the encoder would output -------------------------------------------------------
# Every vessel has its own direction (its identity). All ship noise shares a common component, so that two
# different vessels are not completely unlike each other. Each recording (passage) adds its own offset for the
# conditions it was made in, and each 5-second segment adds a little noise of its own.
common = unit(rng.normal(size=DIM))


def new_vessel():
    return unit(rng.normal(size=DIM))


def segment_embeddings(identity, n_segments=12, condition=0.55, noise=0.35):
    """The embeddings an encoder would give for the segments of one passage of a vessel."""
    offset = condition * unit(rng.normal(size=DIM))                 # this passage's recording conditions
    segs = identity + 0.6 * common + offset + noise * rng.normal(size=(n_segments, DIM)) / np.sqrt(DIM) * 8
    return unit(segs)                                                # the encoder outputs unit vectors


# --- The gallery -------------------------------------------------------------------------------------------
def passage_embedding(segs):
    """One recording's fingerprint: the mean of its segment embeddings, renormalised to length 1."""
    return unit(segs.mean(axis=0))


gallery_vecs, gallery_names = [], []


def enrol(name, passages):
    """Add a vessel's passages to the gallery. The model is not touched: enrolment only appends."""
    for segs in passages:
        gallery_vecs.append(passage_embedding(segs))
        gallery_names.append(name)


def identify(query_segs, top=3):
    """Score a query against every enrolled passage, keep each vessel's best passage, rank the vessels."""
    q = passage_embedding(query_segs)
    scores = np.array(gallery_vecs) @ q                              # cosine similarity: all vectors have length 1
    best = {}
    for name, s in zip(gallery_names, scores):
        best[name] = max(best.get(name, -1.0), float(s))
    ranked = sorted(best.items(), key=lambda kv: -kv[1])[:top]
    decision = ranked[0][0] if ranked[0][1] >= THRESHOLD else "unknown"
    return ranked, decision


def show(title, ranked, decision):
    print(title)
    for i, (name, s) in enumerate(ranked, 1):
        print(f"  {i}. {name:<8} cosine {s:.3f}")
    print(f"  decision: {decision} (threshold {THRESHOLD:.2f})\n")


vessels = {name: new_vessel() for name in ["ALPHA", "BRAVO", "CHARLIE", "DELTA", "ECHO"]}

# Enrol four vessels: several passages for some, one for others.
enrol("ALPHA", [segment_embeddings(vessels["ALPHA"]) for _ in range(3)])
enrol("BRAVO", [segment_embeddings(vessels["BRAVO"]) for _ in range(2)])
enrol("CHARLIE", [segment_embeddings(vessels["CHARLIE"]) for _ in range(1)])
enrol("DELTA", [segment_embeddings(vessels["DELTA"]) for _ in range(1)])
print(f"Gallery: {len(gallery_vecs)} passages of {len(set(gallery_names))} vessels, "
      f"each a {gallery_vecs[0].shape[0]}-number unit vector\n")

# 1. A new passage of an enrolled vessel.
show("Query 1: a new passage of BRAVO", *identify(segment_embeddings(vessels["BRAVO"])))

# 2. A vessel that was never enrolled.
echo_query = segment_embeddings(vessels["ECHO"])
show("Query 2: ECHO, never enrolled", *identify(echo_query))

# 3. Enrol ECHO from two passages. No retraining: the gallery grows by two vectors.
enrol("ECHO", [segment_embeddings(vessels["ECHO"]) for _ in range(2)])
print(f"Enrolled ECHO: the gallery now holds {len(gallery_vecs)} passages of {len(set(gallery_names))} vessels\n")
show("Query 3: the same ECHO passage as query 2", *identify(echo_query))

# 4. Why several passages per vessel help: the same query against one enrolled passage, then against three.
query = segment_embeddings(vessels["ALPHA"], condition=0.9)          # recorded in less familiar conditions
alpha_passages = [v for v, n in zip(gallery_vecs, gallery_names) if n == "ALPHA"]
q = passage_embedding(query)
one = float(alpha_passages[0] @ q)
three = max(float(v @ q) for v in alpha_passages)
print("Query 4: a passage of ALPHA in less familiar conditions")
for label, s in (("1 enrolled ALPHA passage ", one), ("3 enrolled ALPHA passages", three)):
    print(f"  against {label}: best cosine {s:.3f} -> {'ALPHA' if s >= THRESHOLD else 'unknown'}")

Output

Gallery: 7 passages of 4 vessels, each a 512-number unit vector

Query 1: a new passage of BRAVO
  1. BRAVO    cosine 0.608
  2. ALPHA    cosine 0.226
  3. DELTA    cosine 0.195
  decision: BRAVO (threshold 0.50)

Query 2: ECHO, never enrolled
  1. BRAVO    cosine 0.209
  2. DELTA    cosine 0.192
  3. ALPHA    cosine 0.164
  decision: unknown (threshold 0.50)

Enrolled ECHO: the gallery now holds 9 passages of 5 vessels

Query 3: the same ECHO passage as query 2
  1. ECHO     cosine 0.604
  2. BRAVO    cosine 0.209
  3. DELTA    cosine 0.192
  decision: ECHO (threshold 0.50)

Query 4: a passage of ALPHA in less familiar conditions
  against 1 enrolled ALPHA passage : best cosine 0.461 -> unknown
  against 3 enrolled ALPHA passages: best cosine 0.524 -> ALPHA

Query 1 finds BRAVO at 0.608, well clear of the next vessel (0.226). Query 2’s best score is 0.209, below the threshold, so the answer is “unknown”. After ECHO is enrolled from two passages, with nothing else changed, the same recording scores 0.604 and is identified. Query 4 shows the recall lever: against one enrolled ALPHA passage its best score is 0.461 and it would be called unknown; against three, it is 0.524 and ALPHA is found.

Two methods side by side: fingerprint and tonal lines

A learned fingerprint is not the only way to match recordings. The established method reads narrowband tonal lines, the steady frequencies radiated by machinery, from a LOFAR display (low-frequency narrowband line analysis) and compares the line sets. The application runs both on every query: the embedding, and an automated tonal method that extracts up to 20 lines per recording and scores candidates by the fraction of lines that match. The two are shown as two ranked lists, never fused into one number, and when both put the same vessel in their top three, the agreement is flagged.

They are kept apart for a measured reason. A vessel’s tonal lines shift with its shaft and blade rates, so a recording made at a different speed from the enrolment recording is the hardest case for any method that keys on absolute line frequencies, and for a fusion that includes one (Tyagi 2026). In the application, a fixed fusion of the two scores was measured to hurt under such speed (Doppler) changes.

On public recordings, my preprint found the two complementary: their per-query scores agree only moderately, and a fused score put the right vessel first more often. The preprint reports fusion as an experiment, not a recommendation, and lists speed variation as untested (Tyagi 2026).

DEMON (Detection of Envelope Modulation on Noise) is a third, separate analysis: it reads shaft and blade rates from the envelope of cavitation noise. I describe it in my DEMON note, and Oravont’s underwater domain awareness page sets out where it fits.

Evaluating a re-identification system honestly

Re-identification results are easy to overstate. Three rules keep an evaluation honest.

  1. Split by vessel, not by clip. Training and test vessels must not overlap, keyed to vessel identity such as the MMSI (Maritime Mobile Service Identity) or IMO (International Maritime Organization) number (Tyagi 2026). Face recognition is evaluated the same way, on identities absent from training (Schroff et al. 2015; Deng et al. 2019).
  2. Match across recordings, not within one. The gallery and the query for a vessel must come from different passages, and recordings of the same physical transit must not sit on both sides. On public data, scoring recordings of the same transit against each other inflates the results considerably (Tyagi 2026).
  3. Report the open-set measure, not only rank-1. Rank-1 is the share of queries whose correct vessel is ranked first, and it assumes that the vessel is in the gallery. The open-set question is whether genuine matches score above impostors, which the AUC (area under the ROC curve) over same-vessel and different-vessel scores measures; any accept/reject threshold sits on that separation (Tyagi 2026). When a vessel has several gallery passages, retrieval studies also report mean average precision (Ye et al. 2022).

The full protocol and its figures are in the preprint and in the results section of Oravont’s SKANN page; I do not repeat them here.

Practicalities: offline operation and fixed settings

Oravont deploys such systems offline, on the user organisation’s own infrastructure, with no data egress. The application is a self-contained Windows bundle that needs no internet connection and no Python installation, and its interface is served only to the machine it runs on. On an air-gapped machine there is no time synchronisation, so the clock behind its activity log has to be checked by hand.

The signal settings are fixed and must match training. Audio is resampled to 8 kHz mono, cut into 5-second, 40,000-sample segments, and each segment is normalised to zero mean and unit variance; other settings would silently desynchronise the scores. These are the application’s settings, and the same as the preprint’s. (SKANN also has a classification variant, with different filter lengths, for detection and classification; this note covers the identity encoder.)

Questions about vessel re-identification

How do you enrol a new vessel in a re-identification system without retraining the model?

Pass its recordings once through the trained, frozen network and add the resulting fingerprints to the gallery. The network's weights do not change; only the gallery grows, and every later query is compared with it. Enrolling several recordings per vessel helps most.

How does an open-set vessel re-identification system decide that a ship is unknown?

It compares the query's fingerprint with every enrolled recording by cosine similarity and keeps each vessel's best score. If the best score is below a threshold calibrated for that deployment, the answer is unknown: the ship has not been enrolled.

What embedding size and loss work for vessel re-identification from radiated noise?

My SKANN encoder produces a 512-number embedding for each 5-second segment, scaled to length 1 and trained with an additive angular-margin (ArcFace) loss. The training classification layer is discarded, and recordings are compared by cosine similarity. The preprint, arXiv:2609.07399, gives the full training set-up.

Glossary

Embedding
A fixed-length list of numbers (here 512) that a network computes from a recording; recordings of the same vessel should give embeddings that point in similar directions.
Passage
One recording, or encounter, of a vessel. Its embedding is the mean of its segment embeddings, scaled to length 1.
Gallery
The set of enrolled passage embeddings, each labelled with its vessel, that every query is compared with.
Probe (query)
The recording being identified.
Enrolment
Adding a vessel’s passages to the gallery by passing them once through the frozen network. Nothing is retrained.
Cosine similarity
The cosine of the angle between two embeddings; for vectors of length 1, their dot product. From −1 to 1, higher is more alike.
Open set
A setting in which the correct answer may be “none of the known vessels”, so the system must be able to answer “unknown”.
Rank-1
The share of queries whose correct vessel is ranked first.
Recall
The share of queries for which the right vessel is found.
AUC
Area under the ROC (receiver operating characteristic) curve: the probability that a random genuine comparison scores above a random impostor comparison.
LOFAR
Low-frequency narrowband line analysis: the display and method for reading a vessel’s steady tonal lines.
DEMON
Detection of Envelope Modulation on Noise: reading shaft and blade rates from the envelope of cavitation noise.

Sources cited

  1. Tyagi, S. (2026). Open-Set Vessel Re-Identification from Underwater Ship-Radiated Noise with a Raw-Waveform Selective-Kernel Acoustic Neural Network (SKANN) and a Cross-Passage Evaluation Protocol. arXiv:2609.07399 [eess.AS]. arXiv:2609.07399
  2. Scheirer, W.J., Rocha, A. de R., Sapkota, A. and Boult, T.E. (2013). Toward Open Set Recognition. IEEE Transactions on Pattern Analysis and Machine Intelligence 35(7), 1757–1772. doi:10.1109/TPAMI.2012.256
  3. Schroff, F., Kalenichenko, D. and Philbin, J. (2015). FaceNet: A Unified Embedding for Face Recognition and Clustering. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 815–823. doi:10.1109/CVPR.2015.7298682
  4. Deng, J., Guo, J., Xue, N. and Zafeiriou, S. (2019). ArcFace: Additive Angular Margin Loss for Deep Face Recognition. Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 4690–4699. doi:10.1109/CVPR.2019.00482
  5. Ye, M., Shen, J., Lin, G., Xiang, T., Shao, L. and Hoi, S.C.H. (2022). Deep Learning for Person Re-Identification: A Survey and Outlook. IEEE Transactions on Pattern Analysis and Machine Intelligence 44(6), 2872–2893. doi:10.1109/TPAMI.2021.3054775
  6. Irfan, M., Zheng, J., Ali, S., Iqbal, M., Masood, Z. and Hamid, U. (2021). DeepShip: An underwater acoustic benchmark dataset and a separable convolution based autoencoder for classification. Expert Systems with Applications 183, 115270. doi:10.1016/j.eswa.2021.115270
  7. Santos-Domínguez, D., Torres-Guijarro, S., Cardenal-López, A. and Pena-Gimenez, A. (2016). ShipsEar: An underwater vessel noise database. Applied Acoustics 113, 64–69. doi:10.1016/j.apacoust.2016.06.008

The figures on this page are drawn for it; the worked example’s numbers come from its synthetic model, not from any recording.

Related: DEMON analysis · Underwater acoustics for machine-learning engineers · SKANN in brief (oravontsystems.com)