Kinase selectivity · Research report

SeqALigSeqB v2: predicting which of two proteins a compound prefers

Revision 1.2, 13 August 2026. Limitations and the novelty stratification of this test set: kinasefoundationmodel.com/v2/limitations.html.

A random forest that takes two protein sequences and one ligand in a fixed order and returns which protein binds the compound more potently. Trained on the KKB, tested on ChEMBL measurements the model has never seen.

10 August 2026 4,340,117 training comparisons 3,137,588 test comparisons

1. What goes in

The model in one picture: the ABL1 sequence, dasatinib and the GSK3B sequence enter a random forest in a fixed order, and it returns a graded answer running from protein A has greater affinity to the ligand at one end to protein B has greater affinity at the other.
Figure 1. The question the model answers, drawn on the worked example of section 3. Each sequence becomes 480 numbers through ESM2 and the ligand becomes 1,038 through a Morgan count fingerprint and 14 descriptors. The three blocks enter the forest in a fixed order and it returns two probabilities that sum to 1. Structures: ABL1 from PDB 2GQG, which has dasatinib bound, and GSK3B from PDB 1Q5K.

One row is one question: given this compound, does protein A or protein B bind it more tightly? The two proteins occupy fixed positions, and the position is the question. Sequence A, then the ligand, then sequence B.

sequence A480 values
ligand1,038 values
sequence B480 values

indices 0:480 · 480:1518 · 1518:1998  ·  total width 1,998

Protein blocks

ESM2-t12-35M, mean-pooled over residues, first 480 dimensions. Blocks 1 and 3 use the identical encoder, so the only thing distinguishing them is which slot they sit in. All 729 sequence vectors were taken from the precomputed set; none had to be recomputed and none were missing.

Ligand block

Morgan count fingerprint, radius 2, 1,024 bits, followed by 14 descriptors: molecular weight, heavy atom count, bond count, rotatable bonds, ring count, and the counts of C, N, O, S, F, Cl, Br, I and P.

No protein structure, no docked pose, no model of the binding site. Sequence and compound only.

2. What comes out

Two numbers that sum to 1, and they are never collapsed into one.

Class 1

Protein A binds the ligand more potently than protein B.

Class 0

Protein B binds it more potently than protein A.

A fully committed prediction reads 0, 1 or 1, 0. Anything between is the model reporting how sure it is, and that spread is not decoration: section 4 shows accuracy climbing from 58% to 99% across it on the test set. The winner is whichever of the two values is larger; both are kept.

3. A worked example

One comparison drawn from the training set, given in full so it can be redrawn and re-derived independently. Both proteins are human wild-type kinases whose sequences match the canonical UniProt entries exactly, verified by md5, and both have deposited structures. The compound is an approved drug.

Sequence A

Protein
Tyrosine-protein kinase ABL1
Gene
ABL1
UniProt
P00519 (ABL1_HUMAN)
Length
1,130 aa
PDB
2GQG
md5
d24f1ea01ac4c8d96e75dd9550a0fb09

2GQG is dasatinib bound to the activated ABL kinase domain, so the ribbon and the ligand in this example can come from the same structure.

Sequence B

Protein
Glycogen synthase kinase-3 beta
Gene
GSK3B
UniProt
P49841 (GSK3B_HUMAN)
Length
420 aa
PDB
1Q5K
md5
e93d83c1970d960dfc0bf3c997157eef

1Q5K is GSK-3 beta with an inhibitor bound. Dasatinib is not a GSK3B drug, which is the point of the comparison.

Ligand

Name
Dasatinib (BMS-354825, Sprycel)
Formula
C22H26ClN7O2S, MW 488.02
InChIKey
ZBNZXTGUTAYRHI-UHFFFAOYSA-N
CAS
302962-49-8

SMILES, complete

Cc1nc(Nc2ncc(s2)C(=O)Nc3c(C)cccc3Cl)cc(n1)N4CCN(CCO)CC4

In this dataset dasatinib has been measured against 25 of the well-studied kinases, so it is a broadly profiled compound rather than a single-paper result.

The question, the truth and the model's answer
InputABL1 sequence, dasatinib, GSK3B sequence
Measured outcomeABL1 is the more potent target, by at least 4.3 logs
Model outputp0 = 0.0263   p1 = 0.9737
CallSequence A, which is ABL1. Correct.
Output strength0.9473, in the top band where test accuracy is 99.7%

Presented the other way round, as GSK3B, dasatinib, ABL1, the two outputs exchange places and the model names GSK3B the loser. The answer follows the order the question is asked in, which is the whole design.

Sequence A, ABL1, P00519, complete

MLEICLKLVGCKSKKGLSSSSSCYLEEALQRPVASDFEPQGLSEAARWNSKENLLAGPSENDPNLFVALYDFVASGDNTLSITKGEKLRVLGYNHNGEWCEAQTKNGQGWVPSNYITPVNSLEKHSWYHGPVSRNAAEYLLSSGINGSFLVRESESSPGQRSISLRYEGRVYHYRINTASDGKLYVSSESRFNTLAELVHHHSTVADGLITTLHYPAPKRNKPTVYGVSPNYDKWEMERTDITMKHKLGGGQYGEVYEGVWKKYSLTVAVKTLKEDTMEVEEFLKEAAVMKEIKHPNLVQLLGVCTREPPFYIITEFMTYGNLLDYLRECNRQEVNAVVLLYMATQISSAMEYLEKKNFIHRDLAARNCLVGENHLVKVADFGLSRLMTGDTYTAHAGAKFPIKWTAPESLAYNKFSIKSDVWAFGVLLWEIATYGMSPYPGIDLSQVYELLEKDYRMERPEGCPEKVYELMRACWQWNPSDRPSFAEIHQAFETMFQESSISDEVEKELGKQGVRGAVSTLLQAPELPTKTRTSRRAAEHRDTTDVPEMPHSKGQGESDPLDHEPAVSPLLPRKERGPPEGGLNEDERLLPKDKKTNLFSALIKKKKKTAPTPPKRSSSFREMDGQPERRGAGEEEGRDISNGALAFTPLDTADPAKSPKPSNGAGVPNGALRESGGSGFRSPHLWKKSSTLTSSRLATGEEEGGGSSSKRFLRSCSASCVPHGAKDTEWRSVTLPRDLQSTGRQFDSSTFGGHKSEKPALPRKRAGENRSDQVTRGTVTPPPRLVKKNEEAADEVFKDIMESSPGSSPPNLTPKPLRRQVTVAPASGLPHKEEAGKGSALGTPAAAEPVTPTSKAGSGAPGGTSKGPAEESRVRRHKHSSESPGRDKGKLSRLKPAPPPPPAASAGKAGGKPSQSPSQEAAGEAVLGAKTKATSLVDAVNSDAAKPSQPGEGLKKPVLPATPKPQSAKPSGTPISPAPVPSTLPSASSALAGDQPSSTAFIPLISTRVSLRKTRQPPERIASGAITKGVVLDSTEALCLAISRNSEQMASHSAVLEAGKNLYTFCVSYVDSIQQMRNKFAFREAINKLENNLRELQICPATAGSGPAATQDFSKLLSSVKEISDIVQR

Sequence B, GSK3B, P49841, complete

MSGRPRTTSFAESCKPVQQPSAFGSMKVSRDKDGSKVTTVVATPGQGPDRPQEVSYTDTKVIGNGSFGVVYQAKLCDSGELVAIKKVLQDKRFKNRELQIMRKLDHCNIVRLRYFFYSSGEKKDEVYLNLVLDYVPETVYRVARHYSRAKQTLPVIYVKLYMYQLFRSLAYIHSFGICHRDIKPQNLLLDPDTAVLKLCDFGSAKQLVRGEPNVSYICSRYYRAPELIFGATDYTSSIDVWSAGCVLAELLLGQPIFPGDSGVDQLVEIIKVLGTPTREQIREMNPNYTEFKFPQIKAHPWTKVFRPRTPPEAIALCSRLLEYTPTARLTPLEACAHSFFDELRDPNVKLPNGRDTPALFNFTTQELSSNPPLATILIPPHARIQAAASTPTNATAASDANTGDRGQTNNAASASASNST

4. The data

A comparison exists wherever the same compound has been measured against two different protein sequences and the two readings can be ordered. Greater-than and less-than readings are used, not discarded: a compound that is weaker than 10 µM against one protein and 1 nM against another has a decidable winner. Pairs whose readings overlap, so that no winner can be named, are not used.

Training set, from the KKB
Source measurements841,187
Dropped, sequence has no embedding29
Rows remaining841,158
Repeated (sequence, ligand) readings combined64
Readings after combining repeats841,094
Comparisons4,340,117
Rows presented to the forest, after order swap8,680,234
Pairs set aside, readings overlap35,732,088
Distinct ligands / sequences / genes99,739 / 728 / 497
Withheld from training0

Every comparison is presented twice, once as A, ligand, B with its answer and once as B, ligand, A with the answer reversed. This is what forces the model to read the order: a model that ignored which slot a protein occupied would give the same answer to both versions and be wrong on exactly one of them every time, scoring 50% regardless of how well it understood the chemistry. Protein A is the winner in 49.0% of comparisons before the swap and exactly 50.0% after, so no model can profit from guessing a slot.

Test set, from ChEMBL
ChEMBL measurements mapping to our sequences869,296
Comparisons built4,544,589
Removed, the triplet already exists in training1,407,001
Test comparisons3,137,588
Distinct ligands / sequences32,887 / 482
Protein A is the winner49.6%

The exclusion operates on the whole triplet, protein-ligand-protein, because that is the unit the model is asked about. A ChEMBL comparison is removed only when that exact three-way combination is already in training. Nothing is removed for sharing a protein, and nothing for sharing a protein-and-compound pair: if ChEMBL pairs a familiar compound against a different second protein, that is a question the model has never been asked and it is kept. Potency is computed from the reported value in nanomolar as 9 - log10(nM) rather than from ChEMBL's pchembl_value, which is left empty on every greater-than and less-than row and would have silently discarded 311,000 qualified readings. Ki and Kd measurements are used, and both readings in a comparison must be of the same measurement type, so a Ki is never weighed against an IC50.

5. Train and test performance

Training accuracy
94.3%
4,340,117 comparisons
Test accuracy
75.3%
3,137,588 comparisons
Gap
19.0
percentage points

The model reproduces its training data at 94.3% and generalises to unseen ChEMBL comparisons at 75.3%. Both are measured on the same two quantities, accuracy against the known winner, over the full sets rather than a sample.

Accuracy by the size of the true potency difference
Separation Train nTrain Test nTest
Under 0.5 log1,346,15087.6%696,09657.7%
0.5 to 1 log1,083,23295.1%761,49871.5%
1 to 2 log1,154,85898.1%1,067,91081.0%
Over 2 log755,87799.5%612,08490.2%

Both curves run the same direction. The further apart the two proteins actually are in potency, the more reliably the model calls it, reaching 90.2% on the test set where the separation exceeds two logs. Where the two proteins are within half a log of each other, the test accuracy of 57.7% is close to a coin flip.

Accuracy by the model's own confidence
Spread between the two outputs Train nTrain Test nTest
0.0 to 0.2543,10169.3%1,075,80057.9%
0.2 to 0.4717,26391.7%906,26174.4%
0.4 to 0.6925,21698.2%650,02888.4%
0.6 to 0.81,063,43599.7%368,75596.4%
0.8 to 1.01,091,102100.0%136,74499.3%

This is the practical result. The two outputs are not merely a label in disguise; the distance between them tracks how often the model is right. On unseen ChEMBL data, predictions where the two values are far apart are correct 99.3% of the time, and predictions near a coin flip are correct 57.9% of the time. The model flags its own uncertain calls. On the test set, 505,499 comparisons carry a spread above 0.6 and are correct 97.2% of the time taken together.

6. Order reversal

Exchanging the two proteins should reverse the answer. Measured on the output values rather than on the winner label, the mean of |p(1 | A,B) - p(0 | B,A)| would be zero for a perfectly antisymmetric model.

TrainingTest
Antisymmetry error0.0180.025
Declared winner changes on swap98.7%95.1%

The swap augmentation did what it was there to do. Presenting the same comparison the other way round moves the two outputs almost exactly to their mirror positions and changes the declared winner in 95.1% of unseen cases.

7. Prediction strength and accuracy

Prediction strength is the larger of the two output probabilities, so it runs from 0.5, a coin flip, to 1.0, a fully committed call. This is the same definition used in the LigASeqLigB report, so the two models can be read side by side. Accuracy rises with strength on both sets, and it never falls at any step.

Two stacked panels. The upper panel plots accuracy against prediction strength for the training set and the ChEMBL test set; both curves rise steadily and a dashed vertical line marks the operating point at 0.70, where the test curve reaches 92.3 percent. The lower panel plots the share of comparisons still answerable at each cutoff; at 0.70 the test set retains 37 percent.
Figure 2. The upper panel is what you gain by raising the cutoff. The lower panel is what it costs. The dashed line at 0.70 is the operating point argued for below.
Accuracy within each band of prediction strength
Prediction strength Train nShareAccuracy Test nShareAccuracy
0.5 to 0.6543,10112.5%69.3%1,075,80034.3%57.9%
0.6 to 0.7717,26316.5%91.7%906,26128.9%74.4%
0.7 to 0.8925,21621.3%98.2%650,02820.7%88.4%
0.8 to 0.91,063,43524.5%99.7%368,75511.8%96.4%
0.9 to 1.01,091,10225.1%100.0%136,7444.4%99.3%

Acting only on predictions at or above a threshold. The point of this second table is the trade: accuracy rises as the threshold rises, but the number of comparisons you can still answer falls away. The right cutoff is where the accuracy is worth the predictions you give up.

Act only at strength
Act only at strength Train keptShareAccuracy Test keptShareAccuracy
0.50, answer everything4,340,117100.0%94.3%3,137,588100.0%75.3%
0.603,797,01687.5%97.9%2,061,78865.7%84.4%
0.703,079,75371.0%99.4%1,155,52736.8%92.3%
0.802,154,53749.6%99.8%505,49916.1%97.2%
0.901,091,10225.1%100.0%136,7444.4%99.3%

On the test set the accuracy gained per prediction surrendered falls off after 0.70. Going from 0.50 to 0.70 buys 17 accuracy points and costs 63% of the answers. Going from 0.70 to 0.90 buys only 7 more points and costs 88% of what remained. On that reading 0.70 is the useful operating point: more than a third of all comparisons answered at 92.3%, against 75.3% if you answer everything.

Where the shortlist must not contain mistakes, 0.80 answers 505,499 comparisons at 97.2%. Beyond 0.90 the gain is fractions of a point for most of the remaining data, so there is little reason to go further.

Note that the strength value does not mean the same thing on the two sets. On training comparisons a 0.70 prediction is right 99% of the time; on ChEMBL the same 0.70 is right 92% of the time. The ordering is reliable, the absolute number is not transferable, which is why the cutoff must be set from the test column and not the training one.

8. Methods

Building a comparison

Each measurement is converted to a range on pIC50, where higher is more potent. An exact value gives a point. A less-than reading against a concentration establishes that the compound is more potent than that value and gives a lower bound. A greater-than reading gives an upper bound. A single-concentration screen is treated the same way, potent if it registered as active and weak if it did not. Repeat measurements of the same compound against the same sequence are combined into one range, with exact values taking precedence over bounds and the intersection taken otherwise.

Two ranges form a usable comparison when they cannot trade places, that is when the lower bound of one exceeds the upper bound of the other. Everything else is set aside, because no winner can be named. The recorded separation is the guaranteed minimum, the distance between the two bounds rather than between point estimates.

Featurisation

Protein sequences are keyed by the md5 of the sequence, so a mutant is a target in its own right rather than being folded into its wild type. Vectors were verified before use to be the plain full-sequence mean pool rather than a pocket-pooled or domain-pooled variant, reproducing a freshly computed mean pool to a maximum absolute difference of 0.0 across all 733 available. Nine sequences have no embedding and were excluded along with the 29 measurements that reference them; this is the only inclusion criterion applied.

Order augmentation

Every comparison is written into the design matrix twice, as A, ligand, B with its answer and as B, ligand, A with the answer inverted. The two copies are adjacent halves of one matrix and are fitted together in a single call.

Split policy

Nothing is withheld from the KKB. The test set is external, drawn from ChEMBL, and is excluded on the triplet: a ChEMBL comparison is discarded only when that same protein-ligand-protein combination is already present in training. Sequence order is ignored when matching, because a training comparison is presented in both directions, so encountering it either way counts as having seen it.

Fitting

Estimator
sklearn.ensemble.RandomForestClassifier
n_estimators
200
min_samples_leaf
8
max_features
sqrt
random_state
1
bootstrap
True (sklearn default)
class_weight
None; the swap makes the classes exactly balanced
Design matrix
8,680,234 × 1,998 float32, 64.6 GB, built in memory
Fit time
1,502 s on 28 cores
Peak memory
about 85 GB
Cap applied
none
Environment
Python 3.10.15, scikit-learn 1.7.2, numpy 2.2.6, rdkit 2025.09.5, macOS arm64

Reproducing or extending the fit

The pipeline is deterministic. With the same inputs and random_state=1 it produces the same forest, so a rerun is a check rather than a new result. The stages run in order: 300 builds the KKB comparisons, 301 builds the ChEMBL test set, 302 marks everything for training, 303 fits and scores the test set, 304 scores the training set, 305 measures output strength, 306 completes the export.

Adding trees to this forest without refitting it. The trees are independent, so an existing forest can be enlarged. Set warm_start=True on the loaded model, raise n_estimators, and call fit with the identical design matrix and labels. Only the new trees are grown; the existing 200 are left untouched. The matrix must be identical, since sklearn does not check and a changed matrix would give a forest whose trees disagree about what they were trained on. Rebuilding that matrix takes about 70 seconds, and each additional 100 trees costs roughly 750 seconds at this size.

Adding new measurements. A random forest has no incremental update for new rows; partial_fit does not exist for this estimator. New KKB or ChEMBL data therefore requires a complete refit, which is the 1,502 second path above. Rerun 300 so the comparison set is rebuilt from the enlarged source, then 302 and 303. Because the comparison count grows roughly with the square of the number of sequences a compound has been measured against, adding a broadly profiled compound adds far more comparisons than adding a singleton, and the design matrix should be re-sized before the run rather than discovered mid-fit.

9. The exported model

The release is named SeqALigSeqB_v2_selectivity and sits in export/, which is excluded from Dropbox so it does not sync. It is self-contained and packaged so a third party can test it and stand it up on another machine.

export/SeqALigSeqB_v2_selectivity/
FileSizePurpose
model.joblib11.7 GBthe fitted forest
sequence_vectors.npz1.3 MB728 × 480 embeddings, so no ESM2 pass is needed
sequence_index.json29 KBmd5 of sequence to row
predict.py3 KBloader and scorer, asserts the 1,998 width
selftest.py1.5 KBrun first on any new machine
reference_predictions.json82 KB50 recorded predictions the self-test replays
requirements.txt200 Bexact pinned versions
MANIFEST.json1.5 KBfeature order, encoders, counts, hyperparameters
README.txt2.6 KBinput, output, scope and deployment steps

Standing it up elsewhere

Copy the directory, install the pinned versions, then run python selftest.py and require it to print ALL CHECKS PASSED before trusting any number from the model on that machine. The pinning is not a formality: a random forest is a pickled object graph, and a different scikit-learn will either refuse to load it or load it and score differently. The pins are scikit-learn 1.7.2, numpy 2.2.6, joblib 1.5.3 and rdkit 2025.09.5 on Python 3.10.15.

The self-test replays 50 reference comparisons chosen to span the confidence range, from near coin flips to fully committed calls, and requires each to match to within 1e-6. It then exchanges the two sequences on every case and checks that the outputs mirror. Run here against the finished model it reports a maximum absolute difference of 3.3e-16, which is floating-point noise, and passes.

Loading takes about a minute and holds roughly 12 GB of RAM resident, which is the practical constraint on where this can be deployed.

sequence_vectors.npz covers only the 728 sequences the model was trained on. A sequence outside that set raises an error rather than being scored against a guessed vector.

Every KKB comparison was used for training, including all 5,846 in which a wild type is compared against one of its own mutants. Because nothing was withheld and ChEMBL is keyed by gene and therefore contains no mutants, this release has no measured wild-type-versus-mutant accuracy and none can be quoted for it. The model is trained on those comparisons and will predict on them; that capability is unmeasured rather than absent. This is recorded in MANIFEST.json and in split_counts.json so it travels with the files.